2012-03-05 31 views
4

上下文:多用戶應用程序(的node.js) - 1個畫家,正客戶最小化畫布 「位圖」 的數據大小

帆布尺寸: 650x400像素(= 260000像素)

對於畫布會經常更新(我每秒思考10次),所以我需要保持儘可能小的數據大小,特別是在考慮上傳速率時。

返回base64字符串的toDataURL()方法很好,但它包含我甚至不需要的大量數據(每像素23位)。它的長度是8,088(沒有前面的MIME信息),並假設JavaScript字符串具有8位編碼,即8.1千字節數據,每秒10次。

我的下一個嘗試使用JS對象用於不同環境的行動像moveTo(x, y)lineTo(x, y),將它們發送到服務器,並有客戶收到增量更新數據(通過時間戳)。然而,這比base64字符串效率更低。

{ 
    "timestamp": 0, 
    "what": { 
     "name": LINE_TO, 
     "args": {"x": x, "y": y} 
    } 
} 

,因爲有近300 lineTo命令已經當您聯繫您的刷卡刷它不流利,也沒有精確的工作。有時候會有一部分運動缺失(使一條直線而不是四捨五入),有時事件甚至不會被腳本客戶端識別,因爲它似乎已經被已經觸發的大量事件「淹沒」了。

所以我必須最終使用8.1 KB的base64字符串。我不想擔心這一點 - 但即使與增量更新異步完成,實際服務器上也會出現嚴重滯後現象,更不用說偶爾帶寬超限了。

我使用的唯一顏色是#000和#FFF,所以我只考慮1位數據結構,只有增量更新。這基本上就足夠了,我不介意任何「顏色」精度損失(畢竟它是黑色)。

由於大部分畫布都是白色的,您可以考慮使用額外的Huffman遊程編碼來進一步縮小尺寸。像的尺寸50X2像素中,並在一個單一的黑色像素畫布(26,2)將返回以下字符串:75W1B74W(50個+ 25的白色像素,然後1個黑色像素,則24個白色像素)

它即使倘使畫布由1位串這樣的:

00000000000000000000000000000000000000000000000000 
00000000000000000000000001000000000000000000000000 

這將有很大的幫助了。

我的第一個問題是:如何寫一個算法來獲得這個數據高效

二是:我怎麼能(通過節點服務器)通過純二進制數據帆布的客戶呢?我怎樣才能將1位數據結構發送到服務器?我需要將我的位轉換爲十六進制(或更多)的數字並重新解析嗎?

是否可以使用this作爲數據結構?

由於提前,

哈爾蒂

回答

1

我最後整理出來了。我使用了一種算法來獲取指定區域(即當前繪製的區域)內的圖像數據,然後將圖像數據粘貼到相同的座標上。

  1. 雖然畫,我把我的應用程序瞭解改性區有多大,它開始的地方(存儲在currentDrawingCoords)。

  2. pixels是通過調用context.getImageData(left, top, width, height)與存儲的繪圖座標獲得的ImageData數組。

  3. getDeltaUpdate是在onmouseup稱爲(是的,這是區域構思的缺點):(...|w,b,w,b,w,[...]

    getDeltaUpdate = function(pixels, currentDrawingCoords) { 
        var image = "" + 
         currentDrawingCoords.left + "," + // x 
         currentDrawingCoords.top + "," + // y 
         (currentDrawingCoords.right - currentDrawingCoords.left) + "," + // width 
         (currentDrawingCoords.bottom - currentDrawingCoords.top) + ""; // height 
        var blk = 0, wht = 0, d = "|"; 
    
        // http://stackoverflow.com/questions/667045/getpixel-from-html-canvas 
        for (var i=0, n=pixels.length; i < n; i += 4) { 
          if(
           pixels[i] > 0 || 
           pixels[i+1] > 0 || 
           pixels[i+2] > 0 || 
           pixels[i+3] > 0 
          ) { 
           // pixel is black 
           if(wht > 0 || (i == 0 && wht == 0)) { 
            image = image + d + wht;   
            wht = 0; 
            d = ","; 
           } 
           blk++; 
    
           //console.log("Pixel " + i + " is BLACK (" + blk + "-th in a row)"); 
          } else { 
           // pixel is white 
           if(blk > 0) { 
            image = image + d + blk;   
            blk = 0; 
            d = ","; 
           } 
           wht++; 
    
           //console.log("Pixel " + i + " is WHITE (" + blk + "-th in a row)"); 
          } 
        } 
    
        return image; 
    } 
    
  4. image是具有報頭部分(x,y,width,height|...)和數據本體部分的字符串)

  5. 結果是具有比所述的base64串少字符的字符串已經(相對於8K的字符串,增量更新具有1K-6K字符,依賴新生丁多少東西被抽入修改區)

  6. 該字符串被髮送到服務器,被推到所有其他客戶端和使用getImageData復歸的ImageData:

    getImageData = function(imagestring) { 
        var data = imagestring.split("|"); 
        var header = data[0].split(","); 
        var body = data[1].split(","); 
    
        var where = {"x": header[0], "y": header[1]}; 
        var image = context.createImageData(header[2], header[3]); // create ImageData object (width, height) 
    
        var currentpixel = 0, 
        pos = 0, 
        until = 0, 
        alpha = 0, 
        white = true; 
    
        for(var i=0, n=body.length; i < n; i++) { 
         var pixelamount = parseInt(body[i]); // amount of pixels with the same color in a row 
    
         if(pixelamount > 0) { 
          pos = (currentpixel * 4); 
          until = pos + (pixelamount * 4); // exclude 
    
          if(white) alpha = 0; 
          else alpha = 255; 
    
          while(pos < until) { 
           image.data[pos] = 0; 
           image.data[pos+1] = 0; 
           image.data[pos+2] = 0; 
           image.data[pos+3] = alpha;     
           pos += 4; 
          } 
          currentpixel += pixelamount; 
          white = (white ? false : true); 
         } else { 
          white = false; 
         } 
        } 
    
        return {"image": image, "where": where}; 
    } 
    
  7. 呼叫context.putImageData(data.image, data.where.x, data.where.y);把這個區域放在所有東西上面!

正如前面提到的,這可能不是每一種單色畫布繪製應用的完美套裝,因爲修改區域只提交onmouseup。但是,我可以忍受這種折衷,因爲它比服務器上的所有其他方法的壓力要小得多。

我希望我能幫助人們去關注這個問題。

2

我需要保持數據尺寸儘可能小

那就不要發送整個數據。只發送更改,接近你自己提出的建議。

製作框架,使每個用戶只能執行「操作」,例如「從X1,Y1到X2,Y2繪製黑色筆畫寬度2」。

我不會打擾一些純二進制的東西。如果只有兩種顏色,那麼可以很容易地發送字符串「1,2,x,y,x2,y2」,其他人將以與本地客戶端完全相同的方式進行解析,並且將以相同方式繪製。

我不會過時這一點。在擔心任何聰明的編碼之前,先用簡單的字符串來處理它。值得首先嚐試簡單的事情。也許這個表現會很好,不會遇到很多麻煩!

+0

是的,這也是我第一次想到的(我也是這樣做的)。 我使用的對象類似如下: '{ \t 「時間戳」:0, \t 「什麼」:{ \t \t 「名」:LINE_TO, \t \t 「ARGS」:{ 「X」: X, 「Y」 爲:y} \t \t} }' 然而,大約60X40像素的單刷線,你有超過300個這樣的命令了。 但是,簡單(較短)的字符串而不是對象的想法聽起來也很不錯。 對象對我來說並不是很流利(在localhost上)。 Base64字符串確實可以流暢地工作,但恐怕只有LAN節點服務器纔是這種情況。 – Harti 2012-03-06 10:12:47