如何撤消 HTML5 Canvas 绘图应用程序的橡皮擦操作

Posted

技术标签:

【中文标题】如何撤消 HTML5 Canvas 绘图应用程序的橡皮擦操作【英文标题】:How to Undo the eraser action for HTML5 Canvas Drawing App 【发布时间】:2016-04-06 19:32:23 【问题描述】:

我创建了一个 html5 绘图应用程序,它具有允许用户选择要绘制的颜色、更改绘图工具的大小(半径)、撤消、重做和完全清除画布的基本功能。

我最近添加了一个使用 globalCompositionProperty (destination-out) 的橡皮擦工具来擦除画布的选定区域。这部分工作正常,但是当我去撤消擦除时,整个画布都被清除了,重做功能不起作用。当我使用常规绘图工具(使用 source-over)恢复绘图时,撤消/重做工作。我已经包含了应用程序中使用的一些功能的代码。每次关闭绘图工具时,我都会存储画布的快照。这被添加到用于撤消和恢复画布的数组中。

有人可以解释我在这里做错了什么吗?我不确定我是否了解 globalCompositeOperation 属性如何影响已保存的画布 DataURI。

function storeSnapshot() 
    cStep++;
    if (cStep < cPushArray.length)  cPushArray.length = cStep; 
    cPushArray.push(canvas.toDataURL());


var putPoint = function(e)
    if(dragging)
    context.lineTo(e.clientX - 174, e.clientY - 50);
    context.stroke();
    context.beginPath();
    if(eraser == true)
        context.globalCompositeOperation="destination-out";

    else
        context.globalCompositeOperation="source-over"; 
    
    context.arc(e.clientX - 174, e.clientY - 50 , radius, 0, 2 * Math.PI);
    context.fill();
    context.beginPath();
    context.moveTo(e.clientX - 174, e.clientY - 50);
    

更新: 在玩了一段时间后,我尝试了一些可以解决我的问题的方法。我不确定这是否是正确的做事方式,但我想我会向你展示我所做的一切。

我有一个在鼠标抬起时触发的脱离功能。这是之前的函数:

var disengage = function()
dragging = false;
context.beginPath();
storeSnapshot();
    

我添加了一些代码,这允许用户使用橡皮擦工具并撤消/重做操作。我还不确定为什么会这样,所以如果有人有见识,我将不胜感激。

var disengage = function()
dragging = false;
context.beginPath();
if(eraser == true)

    context.globalCompositeOperation="source-over"; 

storeSnapshot();
    

【问题讨论】:

当您保存快照时使用var data = ctx.getImageData(0,0,canvas.width,canvas.height) 或更好的方法,仍然只复制您已更改的画布部分。恢复画布ctx.putImageData(data,0,0) 使用 toDataURL 很慢,并且使用 2.5 + 倍以上的内存,因为 Base64 对每个字符编码 6 位,而字符在 javascript 中是 16 位,因此图像中每 6 位浪费 10 位。使用destination-out 是正确的。 【参考方案1】:

我遇到了和你完全相同的问题,很高兴我遇到了你的问题/解决方案,因为这对我来说是一个灯泡时刻。我一看到您的解决方案,就完全明白发生了什么。

为了深入了解您的解决方案为何有效,globalCompositeOperation 是画布的一个属性,它基本上规定了内容(在本例中为线条笔划)如何放置在画布上。默认情况下,它是 source-over,它只是在任何现有笔划之上放置一个新的线条笔划。正如您所提到的,擦除使用destination-out,它将所有现有内容保留在不与新线条重叠的位置(因此是擦除)。对于一些信息丰富的图表和解释,我强烈推荐 globalCompositeOperation 的 MDN 文档:https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation

因此,如果您进入擦除模式(对您而言,eraser = true),擦除,然后立即尝试撤消,globalCompositeOperation 仍将设置为destination-out。这意味着当您的撤消尝试将存储的图像绘制到画布上时,一切都会消失,因为它本质上仍在擦除(正如destination-out所说,它会丢弃与新内容重叠的任何现有内容,在此case 是整个画布空间)。因此,您需要像在解决方案中那样将其设置回源覆盖,以便恢复的图像实际出现在画布上。希望对您有所帮助!

【讨论】:

太棒了!感谢您的反馈和资源。

以上是关于如何撤消 HTML5 Canvas 绘图应用程序的橡皮擦操作的主要内容,如果未能解决你的问题,请参考以下文章

html5 canvas绘图有啥用

如何在 HTML5 画布上绘图

用html5的canvas和JavaScript创建一个绘图程序

使用鼠标在 HTML5 Canvas 上绘图

html5 canvas绘图有啥用

html5中canvas画的图形如何打印以及导出pdf