清除画布矩形(但保留背景)

Posted

技术标签:

【中文标题】清除画布矩形(但保留背景)【英文标题】:Clear Canvas Rect (but keep background) 【发布时间】:2011-08-01 13:43:41 【问题描述】:

我正在尝试为一个圆圈设置动画,然后水平移动它,效果很好。然而,当圆圈移动时,我必须在那个圆圈上做一个 clearRect ,以便它在水平方向上重新绘制它自己。当我做一个 clearRect 时,它也会使背景周围有白框,因此它会在圆圈移动的方向上成为一条白色水平线。

    有没有办法在没有 clearRect 的情况下清除圆圈? 如果我必须在 clearRect 之后继续重绘背景,则当该区域中有 10 个圆圈在移动时,画布会闪烁。

还有其他方法可以解决这个问题吗?

    function drawcircle() 
        clear();    

        context.beginPath();
        context.arc(X, Y, R, 0, 2*Math.PI, false);                  
        context.moveTo(X,Y);            
        context.lineWidth = 0.3;
        context.strokeStyle = "#999999"; 
        context.stroke();

        if (X > 200)
        
            clearTimeout(t); //stop
        
        else
        
            //move in x dir
            X += dX;
            t = setTimeout(drawcircle, 50);
        
    

    function clear() 
        context.clearRect(X-R, Y-R, 2*R, 2*R);
    

【问题讨论】:

画布上只有圆圈吗? 不,不是,我从一些简单的东西开始,然后希望在引入更多圆形或其他形状时使其更通用。我还没有测试过,但也许你可以在它沿着画布移动时及时设置它的可见性?只是一个想法。 【参考方案1】:

基础知识:作为非保留绘图模式图形 API 的 html5 Canvas

首先,让我们讨论一下 HTML5 Canvas 的工作方式。就像带有快干油画颜料的真实画布一样,当您将stroke()fill()drawImage() 放到画布上时,颜料就会成为画布的一部分。尽管您画了一个“圆”并看到它,但圆的像素完全取代了背景(或者在圆边缘的抗锯齿的情况下,与它们混合并永远改变它们)。如果你让莫奈把画中的一个人“移动”到右边一点,他会怎么说?您无法移动圆圈,无法擦除圆圈,无法检测到鼠标悬停在圆圈上……因为没有圆圈,只有单个二维像素数组。

一些选项

    如果您的背景是完全静态的,请通过 CSS 将其设置为 canvas 元素的背景图片。这将显示并覆盖您绘制的内容,但在您清除画布时不会被清除。

    如果你不能做到以上,那么你还不如清除整个画布并重新绘制每一帧。在我的测试中,清除和重绘画布的一部分所需的工作是不值得的,除非重绘画布非常昂贵。

    例如,看这个测试:http://phrogz.net/tmp/image_move_sprites_canvas.html 在 Safari v5.0.4 中,如果我每帧清除并重新绘制整个画布一次,我会看到 59.4fps,如果我使用 20 个 clearRect() 调用和 20 个 drawImage() 调用仅重新绘制脏的部分,我会看到 56.8fps背景每一帧。在这种情况下,聪明并跟踪小的脏区域会变慢

    作为另一种选择,使用保留绘图的图形系统,如 SVG 或 HTML。有了这些,每个元素 都是独立维护的。你可以改变物品的位置,它会神奇地移动;由浏览器以最有效的方式智能地绘制更新。

    您可以通过在同一个 HTML 页面中创建和分层多个画布(使用 CSS 绝对定位和 z-index)来保持自定义画布绘图的强大功能。正如在 this performance test 中看到的那样,移动 20 sprites via CSS 比尝试在单个画布上自行完成要快得多。

闪烁?

你写道:

如果我必须在 clearRect 之后继续重绘背景,那么当该区域中有 10 个圆圈在移动时,画布会闪烁。

这从来不是我的经验。您能否提供一个小示例来说明您声称会发生这种“闪烁”问题(请指定您遇到此问题的操作系统、浏览器和版本)? two comments by prominent browser developers 指出 Firefox 和 Safari 都不应该显示任何闪烁。

【讨论】:

这真的很有帮助,我在实现所有画布内容时根本没有考虑 html/css 方面,这对我来说是一个错误。关于闪烁,我的印象是,因为我需要在不同的时间间隔重新绘制画布,因为不同的东西在画布上移动,所以它可能会导致闪烁,这可能是也可能不是。如果我确实看到任何闪烁,我一定会报告回来。谢谢。 我也注意到我的 Penthium 4 Ubuntu 系统出现闪烁。当在 3 种颜色之间快速切换时,该问题最为普遍。创建具有三种颜色的简单矩形(自然为红色,鼠标按下时为绿色,鼠标松开时为蓝色),如果您快速单击,它看起来像是有不同的红色、绿色和/或蓝色水平条。 @puk 您所描述的是 screen tearing,缺乏垂直同步。双重(或三重或四重)缓冲对此无能为力。它要求渲染 API(Web 浏览器)与显示器的刷新率同步。另见Tearing in HTML5 Canvas。【参考方案2】:

这实际上很容易实现,只需将多个画布放在彼此顶部即可。您可以在背景中的(等待它...)画布上绘制背景,并在前景中的第二个画布上绘制圆圈。 (即堆叠在背景画布前面)

多个画布实际上是提高任何动画性能的最佳方法之一,其中最终图像的元素独立移动并且不一定在每一帧中移动。这允许您避免重绘未在每一帧中移动的项目。但是,要记住的一件事是,更改在不同画布上绘制的项目的相对深度(想想 z-index)现在需要在 dom 中重新排序实际的 <canvas> 元素。实际上,这对于 2D 游戏和动画来说很少是问题。

【讨论】:

【参考方案3】:

与公认的答案相反; 是的,您可以恢复以前的绘制状态,这与其他答案所暗示的相反; ,您不需要额外的画布来这样做:

CanvasRenderingContext2D API 包括函数getImageData()putImageData()。创建背景图像后,将整个内容存储在变量const background = context.getImageData(x, y, width, height)Uint8ClampedArray 类型的简单 RGBA 位图)中,然后在使用clearRect() 或其他任何东西擦拭画布后,只需将该变量传回即可恢复背景图像反方向:context.putImageData(x, y, background)

【讨论】:

【参考方案4】:

有两种方法可以减少闪烁,尤其是如果你有很多圆圈。

一个是双缓冲,关于这个的简短问题,您可以查看: Does HTML5/Canvas Support Double Buffering?

基本上,您在两个画布上绘图,并根据需要将它们换入和换出。

这将是更好的选择,尤其是每帧有很多变化,但是,我这样做的另一种方法是使用背景颜色在我想要擦除的圆圈上绘制,然后用正确的颜色绘制新圈子。

唯一的问题是,您可能会留下一些尝试擦除的证据,因为似乎对于某些形状,很难将其准确地绘制在顶部。

更新:

根据评论,您可以查看有关画布上双缓冲的讨论:

HTML canvas double buffering frame-rate issues

基本的想法是跟踪您绘制的所有内容,使用当前位置,然后在单独的画布上,重新绘制所有内容,然后将它们翻转,然后我会在新位置再次重新绘制,以确保图像看起来完全像它应该的那样。交换它们是一种快速操作,唯一的问题是如果您将事件处理程序放在画布上,在这种情况下,将它们放在画布周围的 div 或 span 上,这样这些信息就不会丢失。

【讨论】:

如果你将圆从一个区域移动到画布上已经绘制背景的另一个区域怎么办。当圆圈进入这个新的背景区域时,它会开始在这个背景上出现白框,这看起来不太好。我想你需要调整背景以适应这种运动。将背景设置为白色可以解决所有 clearRect 问题,但如果有一个透明的 clearRect 或者只是清除绘制的形状本身会很好。 如果你能在任何浏览器上生成任何 HTML5 画布示例内容,我会非常感兴趣,通过两个画布的双缓冲除了减慢你的应用程序之外,还能做任何事情。据我所知,所有 HTML5 画布实现都是双缓冲的,因为 javascript 调用排队/合并,可见更新仅在脚本将控制权返回给浏览器时发生。我粗鲁地拒绝投票,因为我相信这是 FUD 的建议。如果您可以展示问题案例,将投票赞成。 @Phrogz - 仅仅因为画布实现是双缓冲的,就意味着拥有两个画布,一个用于绘制,一个显示,然后翻转,并不是一个坏选择。

以上是关于清除画布矩形(但保留背景)的主要内容,如果未能解决你的问题,请参考以下文章

如何以正确的方式旋转画布

为剪辑区域设置动画时清除画布时出现问题

使用 FabricJS 裁剪图像

更改背景画布 kivy

如何清除画布属性和事件并将其添加回fabricjs?

如何在WPF中绘制具有背景颜色的矩形