2017-08-24 102 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

相关问题