如何在Chrome Windows下的HTML5画布中抗锯齿clip()边缘?

31

我在canvas上使用了clip()函数。

结果: using canvas clip in various browsers

正如你所看到的,Chrome版本的边缘存在可怕的锯齿/走样问题。如何解决这个问题?

复现代码:

http://jsfiddle.net/ZRA76/ :

<canvas id="test" width="300" height="300"></canvas><script type="text/javascript">
    cv = document.getElementById("test");
    ctx = cv.getContext("2d");

    var im = new Image();
    im.onload = function () {
        ctx.beginPath();
        ctx.arc(110, 110, 100, 0, 2*Math.PI, true);
        ctx.clip();
        ctx.drawImage(im, 0, 0);
    }
    im.src = "http://placekitten.com/300/300";
</script>

我也遇到了这个问题。我所做的是在图像后面的同一位置画一个圆,半径比图像大1或2像素。保持颜色相似,这样你就可以得到“抗锯齿”图像剪辑了。 - Automatico
5个回答

26
如果你正在进行复杂的分层绘图,你可以使用globalCompositeOperation在第二个临时画布上模拟剪切。然后,你可以使用drawImage将临时画布复制回原始画布。我不能保证这种方法的性能,但这是我知道的实现你想要的唯一方法。
//set-up - probably only needs to be done once
var scratchCanvas = document.createElement('canvas');
scratchCanvas.width = 100;
scratchCanvas.height = 100;
var scratchCtx = scratchCanvas.getContext('2d');


//drawing code
scratchCtx.clearRect(0, 0, scratchCanvas.width, scratchCanvas.height);

scratchCtx.globalCompositeOperation = 'source-over'; //default

//Do whatever drawing you want. In your case, draw your image.
scratchCtx.drawImage(imageToCrop, ...);


//As long as we can represent our clipping region as a single path, 
//we can perform our clipping by using a non-default composite operation.
//You can think of destination-in as "write alpha". It will not touch
//the color channel of the canvas, but will replace the alpha channel.
//(Actually, it will multiply the already drawn alpha with the alpha
//currently being drawn - meaning that things look good where two anti-
//aliased pixels overlap.)
//
//If you can't represent the clipping region as a single path, you can
//always draw your clip shape into yet another scratch canvas.

scratchCtx.fillStyle = '#fff'; //color doesn't matter, but we want full opacity
scratchCtx.globalCompositeOperation = 'destination-in';
scratchCtx.beginPath();
scratchCtx.arc(50, 50, 50, 0, 2 * Math.PI, true);
scratchCtx.closePath();
scratchCtx.fill();


//Now that we have a nice, cropped image, we can draw it in our
//actual canvas. We can even draw it over top existing pixels, and
//everything will look great!

ctx.drawImage(scratchCanvas, ...);

我们在一个草稿画布中进行这个操作的原因是destination-in是一种相当破坏性的操作。如果你已经在主画布上画了一些东西(也许你放置了一个漂亮的渐变背景),然后想要绘制一个剪切图像,那么剪切圆也会剪切掉你已经画好的所有东西。当然,如果你的情况比较简单(也许你只想画一个剪切图像),那么你可以不用草稿画布。
您可以在我的演示页面上尝试不同的剪切模式。底部行(带有渐变)对您来说不太有用,但顶部行(带有圆形和正方形)更相关。 编辑 哎呀,我不小心forked your JSFiddle以演示这个技巧。

12
我的解决方法是在绘制图像后,以相同的半径绘制一个薄的(2像素)白色描边。这样可以很好地覆盖锯齿,并在各种浏览器中看起来不错。请保留原有HTML标记格式。

我今天已经为此苦苦挣扎了一个多小时,结果发现如此简单且惊人的解决方法 :) - trueicecold
是的,这很聪明,<3! - Adrian Seeley

5

我在使用Chrome和clip()时遇到了同样的问题。

在我的情况下,通过设置画布的globalCompositeOperation属性,我实现了更好的浏览器兼容性。

context.globalCompositeOperation = 'source-atop';

所以首先画出你的形状,本例中是圆形。然后切换到“source-atop”模式并绘制你的小猫图像。

需要注意的是,这是一个简单的绘图方法,假设画布是空白的。之前的画布绘制会影响你的剪辑效果。


这个解决方案表现非常好,谢谢。一个弧形用于剪辑,一个弧形用于描边。简单明了。我认为,除非你不想要描边,否则使用刮擦画布的解决方案会过于复杂和缓慢。 - mattdlockyer
我成功地应用了这个技巧,再加上“back-canvas”技术,来减少在通过画布运行视频时出现的不良锯齿边缘:http://codepen.io/paceaux/pen/egLOeR谢谢你的分享。 - paceaux

2

1
现在,错误修复正在Chrome仪表板的开发状态中。 正如问题中所述,其他浏览器(IE,Firefox,Safari)的clip()是抗锯齿的。 - sapics

-1

使用SVG剪辑。效果很好,但使用起来不太方便。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接