原因
插入beginPath(),因为rect() 添加到路径中,这与fillRect()/strokeRect() 不同。这里发生的情况是矩形的累积最终会随着时间的推移减慢剪裁速度。
这与使用无法与显示器/屏幕刷新同步的setInterval 结合使用会使问题恶化。
详细说明后者:
使用setInterval()/setTimeout() 可能会导致撕裂,当绘图处于其操作的“中间”未完全完成并发生屏幕刷新时会发生这种情况。
setInterval 只能采用整数值,您需要 16.67 毫秒来逐帧同步 (@60Hz)。即使setInterval 可以采用浮点数,它也不会将计时与监视器计时同步,因为计时器机制根本不受监控。
要解决此问题,请始终使用requestAnimationFrame 将绘图与屏幕更新同步。这与监视器刷新直接相关,是一种比其他更底层和更高效的实现,是为此目的而设计的,因此得名。
嵌入上述两个修复的解决方案
见modified bin here。
未来访问者的代码:
function draw() {
context.fillStyle = '#000';
context.fillRect(0, 0, width, height);
context.save();
context.beginPath(); /// add a beginPath here
context.rect(0, 0, 100, 100);
context.clip();
context.fillStyle = '#ff0000';
context.fillRect(0, 0, 200, 200);
context.restore();
requestAnimationFrame(draw); /// use rAF here
}
canvas.width = width;
canvas.height = height;
canvas.style.width = width+'px';
canvas.style.height = height+'px';
requestAnimationFrame(draw); /// start loop
PS:如果你需要停止循环,则在循环内注入一个条件来运行 rAF,即:
if (isPlaying) requestAnimationFrame(draw);
顺便说一句,不需要closePath(),因为这将由the specs 为您隐式完成。您可以根据需要添加它,但调用clip()(或fill() 等)将为您为完成此操作(规范针对浏览器实现者):
计算剪切区域时,打开的子路径必须隐式关闭