将 HTML 画布裁剪为其可见像素(内容)的宽度/高度?

2023-12-25

可以 HTMLcanvas元素被内部裁剪以适应其内容?

例如,如果我有一个 500x500 像素的画布,其中的随机位置只有一个 10x10 像素的正方形,是否有一个函数可以通过扫描可见像素和裁剪将整个画布裁剪为 10x10?


Edit:这被标记为重复项Javascript方法检测PNG不透明区域 https://stackoverflow.com/questions/23255825/javascript-method-to-detect-area-of-a-png-that-is-not-transparent但事实并非如此。该问题详细介绍了如何找到画布中不透明内容的边界,但是不是如何裁剪它。我的问题的第一个词是“裁剪”,所以这就是我想关注的重点。


更好的修剪功能。

尽管给定的答案有效,但它包含潜在的危险缺陷,创建新的画布而不是裁剪现有的画布,并且(链接区域搜索)效率有些低。

如果您有对画布的其他引用,创建第二个画布可能会出现问题,这很常见,因为通常有两个对画布的引用,例如canvas and ctx.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;            
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将 HTML 画布裁剪为其可见像素(内容)的宽度/高度? 的相关文章

随机推荐