将HTML画布裁剪为其可见像素(内容)的宽度/高度? [英] Cropping an HTML canvas to the width/height of its visible pixels (content)?

查看:111
本文介绍了将HTML画布裁剪为其可见像素(内容)的宽度/高度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

可以在内部修剪HTML 画布元素以适合其内容吗?例如,如果我有一个500x500像素的画布,并且在它内部的随机位置只有一个10x10像素的正方形,是否有一个将整个画布裁剪为10x10的函数通过扫描可见像素并裁剪?




编辑:这被标记为重复 Javascript方法来检测PNG的区域这是不透明的,但事实并非如此。该问题详细说明了如何在画布中找到非透明内容的界限,但不是如何裁剪。我的问题的第一个字是裁剪,所以这就是我想要关注的内容。

解决方案

更好的修剪功能。

虽然给出的答案有效,但它包含一个潜在的危险缺陷,创建一个新的画布而不是裁剪现有的画布,并且(链接的区域搜索)效率稍低。 / p>

创建第二个画布可能会产生问题,因为如果您有其他画布引用,这是很常见的,因为通常有两个对画布的引用,例如画布 ctx.canvas 。关闭可能会导致很难删除引用,如果闭包超过了某个事件,则可能永远无法删除该引用。



缺陷是当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是要修剪的画布的2D上下文
// This如果画布不包含或不包含非透明像素,函数将返回false。
//如果画布包含非透明像素,则返回true
函数trimCanvas(ctx){//移除透明边
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;
//从顶部和底部搜索查找包含非透明像素的第一行。 (x = 0; x (y = 0; y< h&!found; y + = 1) $ b if(data [idx1 ++]&&!top){
top = y + 1;
if(bottom){//找到顶部和底部然后停止搜索
found = true;
休息;

$ b if(data [idx2--]&&!bottom){
bottom = h - y - 1;
if(top){//找到顶部和底部,然后停止搜索
found = true;
休息;



if(y> h - y&&!top&&!bottom){return false} //图像完全空白所以什么也不做
}
top - = 1; //正确的顶部
found = false;
//从左侧和右侧搜索,找到包含非透明像素的第一列。
for(x = 0; x 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){//如果左右发现,则停止搜索
found = true;
休息;

$ b if(data [idx2]&&!right){
right = w - x - 1;
if(left){//如果发现左右,则停止搜索
found = true;
休息;
}
}
idx1 + = w;
idx2 + = w;
}
}
left - = 1; //正确的左边
如果(w ===右 - 左+ 1&& h === bottom - top + 1){return true} //如果大小不变则不需要裁剪
w = right - left + 1;
h = bottom - top + 1;
ctx.canvas.width = w;
ctx.canvas.height = h;
ctx.putImageData(imgData,-left,-top);
返回true;
}


Can an HTML canvas element be internally cropped to fit its content?

For example, if I have a 500x500 pixel canvas with only a 10x10 pixel square at a random location inside it, is there a function which will crop the entire canvas to 10x10 by scanning for visible pixels and cropping?


Edit: this was marked as a duplicate of Javascript Method to detect area of a PNG that is not transparent but it's not. That question details how to find the bounds of non-transparent content in the canvas, but not how to crop it. The first word of my question is "cropping" so that's what I'd like to focus on.

解决方案

A better trim function.

Though the given answer works it contains a potencial dangerous flaw, creates a new canvas rather than crop the existing canvas and (the linked region search) is somewhat inefficient.

Creating a second canvas can be problematic if you have other references to the canvas, which is common as there are usually two references to the canvas eg canvas and ctx.canvas. Closure could make it difficult to remove the reference and if the closure is over an event you may never get to remove the reference.

The flaw is when canvas contains no pixels. Setting the canvas to zero size is allowed (canvas.width = 0; canvas.height = 0; will not throw an error), but some functions can not accept zero as an argument and will throw an error (eg ctx.getImageData(0,0,ctx.canvas.width,ctx.canvas.height); is common practice but will throw an error if the canvas has no size). As this is not directly associated with the resize this potencial crash can be overlooked and make its way into production code.

The linked search checks all pixels for each search, the inclusion of a simple break when an edge is found would improve the search, there is still an on average quicker search. Searching in both directions at the same time, top and bottom then left and right will reduce the number of iterations. And rather than calculate the address of each pixel for each pixel test you can improve the performance by stepping through the index. eg data[idx++] is much quicker than data[x + y * w]

A more robust solution.

The following function will crop the transparent edges from a canvas in place using a two pass search, taking in account the results of the first pass to reduce the search area of the second.

It will not crop the canvas if there are no pixels, but will return false so that action can be taken. It will return true if the canvas contains pixels.

There is no need to change any references to the canvas as it is cropped in place.

// 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;            
}

这篇关于将HTML画布裁剪为其可见像素(内容)的宽度/高度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆