2017-08-24 100 views
-1

HTML canvas元素是否可以在內部剪切以適合其內容?例如,如果我有一個500x500像素的畫布,在裏面的隨機位置只有一個10x10像素的正方形,是否有一個函數會通過掃描可見像素和裁剪來裁剪整個畫布到10x10?將HTML畫布裁剪爲其可見像素(內容)的寬度/高度?


編輯:此標記爲的Javascript Method to detect area of a PNG that is not transparent重複,但它不是。該問題詳細說明如何在畫布中找到非透明內容的界限,但不是如何裁剪它。我的問題的第一個字是「裁剪」,所以這就是我想要關注的。

+0

@ K3N編輯解釋了爲什麼它不是重複的。請重新打開。 – WackGet

+1

但是,重新打開後,您只需使用您從該方法獲得的座標,然後使用drawImage()將新的畫布移至區域。 – K3N

回答

0

更好的微調功能。

雖然給出答案的作品它包含了potencial危險的漏洞,創建一個新的畫布,而不是裁剪現有帆布(鏈接區域搜索)是有點低效。

如果您對畫布有其他參考,這是很常見的,因爲通常有兩個對畫布的引用,例如canvasctx.canvas,創建第二個畫布會有問題。關閉可能會導致難以刪除引用,並且如果封閉結束了某個事件,則可能永遠無法刪除該引用。

的缺陷是當畫布不包含像素。允許將畫布設置爲零大小(canvas.width = 0; canvas.height = 0;不會引發錯誤),但某些函數不能接受零作爲參數,並且會引發錯誤(例如ctx.getImageData(0,0,ctx.canvas.width,ctx.canvas.height);是常見做法,但如果畫布沒有大小則會引發錯誤) 。由於這與調整大小沒有直接關係,因此這種潛在的崩潰可以被忽略,並進入生產代碼。

鏈接的搜索檢查每個搜索的所有像素,包括一個簡單的break當發現邊緣會改善搜索時,仍然有平均更快的搜索。同時在兩個方向上搜索,上下左右都會減少迭代次數。而不是爲每個像素測試計算每個像素的地址,您可以通過遍歷索引來提高性能。例如data[idx++]data[x + y * w]

一個更強大的解決方案快得多。

以下函數將使用兩遍搜索從適當位置的畫布裁剪透明邊,同時考慮第一遍的結果以減少第二遍的搜索區域。

如果沒有像素,它將不裁剪畫布,但會返回false以便採取行動。如果畫布包含像素,它將返回true

在剪切到位時,不需要更改對畫布的任何引用。

// ctx is the 2d context of the canvas to be trimmed 
// This function will return false if the canvas contains no or no non transparent pixels. 
// Returns true if the canvas contains non transparent pixels 
function trimCanvas(ctx) { // removes transparent edges 
    var x, y, w, h, top, left, right, bottom, data, idx1, idx2, found, imgData; 
    w = ctx.canvas.width; 
    h = ctx.canvas.height; 
    if (!w && !h) { return false } 
    imgData = ctx.getImageData(0, 0, w, h); 
    data = new Uint32Array(imgData.data.buffer); 
    idx1 = 0; 
    idx2 = w * h - 1; 
    found = false; 
    // search from top and bottom to find first rows containing a non transparent pixel. 
    for (y = 0; y < h && !found; y += 1) { 
     for (x = 0; x < w; x += 1) { 
      if (data[idx1++] && !top) { 
       top = y + 1; 
       if (bottom) { // top and bottom found then stop the search 
        found = true; 
        break; 
       } 
      } 
      if (data[idx2--] && !bottom) { 
       bottom = h - y - 1; 
       if (top) { // top and bottom found then stop the search 
        found = true; 
        break; 
       } 
      } 
     } 
     if (y > h - y && !top && !bottom) { return false } // image is completely blank so do nothing 
    } 
    top -= 1; // correct top 
    found = false; 
    // search from left and right to find first column containing a non transparent pixel. 
    for (x = 0; x < w && !found; x += 1) { 
     idx1 = top * w + x; 
     idx2 = top * w + (w - x - 1); 
     for (y = top; y <= bottom; y += 1) { 
      if (data[idx1] && !left) { 
       left = x + 1; 
       if (right) { // if left and right found then stop the search 
        found = true; 
        break; 
       } 
      } 
      if (data[idx2] && !right) { 
       right = w - x - 1; 
       if (left) { // if left and right found then stop the search 
        found = true; 
        break; 
       } 
      } 
      idx1 += w; 
      idx2 += w; 
     } 
    } 
    left -= 1; // correct left 
    if(w === right - left + 1 && h === bottom - top + 1) { return true } // no need to crop if no change in size 
    w = right - left + 1; 
    h = bottom - top + 1; 
    ctx.canvas.width = w; 
    ctx.canvas.height = h; 
    ctx.putImageData(imgData, -left, -top); 
    return true;    
} 
+0

哇,這個工作,即使'fillText',我遇到了使用其他方法的問題(見我對其他答案的評論)直接工作。這是一個工作演示:http://jsfiddle.net/umasz04w/謝謝。 – WackGet

+0

好吧,但你爲什麼要調用兩次getImageData?把你用putImageData – Kaiido

+0

@Kaiido的裁剪參數進行分析的那個人抱歉錯過了。將修復 – Blindman67

2

能的HTML畫布元素在內部裁剪以適合其內容?

是的,使用this method(或類似的)會給你所需要的座標。背景不必是透明的,而是統一的(修改代碼以適應背景)以供實際使用。

當獲得的座標簡單地使用drawImage()呈現出該區域:

(因爲沒有代碼被所討論提供,採用根據需要):

// obtain region here (from linked method) 
var region = { 
    x: x1, 
    y: y1, 
    width: x2-x1, 
    height: y2-y1 
}; 

var croppedCanvas = document.createElement("canvas"); 
croppedCanvas.width = region.width; 
croppedCanvas.height = region.height; 

var cCtx = croppedCanvas.getContext("2d"); 
cCtx.drawImage(sourceCanvas, region.x, region.y, region.width, region.height, 
          0, 0, region.width, region.height); 

現在croppedCanvas僅包含原始畫布的裁剪部分。

+0

謝謝。相關的問題當然有幫助,但我不知道如何真正提取該地區。 – WackGet

+0

使用'fillText'時,我無法使用鏈接方法查找邊界。如果我使用'0,0'作爲'fillText'座標,則「右邊緣」不正確。如果我將它們改爲例如'1,0',那麼右邊是正確的。看到這個小提琴:http://jsfiddle.net/9jj7o5az/2/ – WackGet

相關問題