探究 canvas 绘图中撤销(undo)功能的实现方式详解

2020-04-20 17:10:15易采站长站整理

最近在做网页版图片处理相关的项目,也算是初入了 canvas 的坑。项目需求中有一个给图片添加水印的功能。我们知道,在浏览器端实现图片添加水印功能,通常的做法就是使用

canvas
drawImage
方法。对于普通的合成(比如一张底图和一张 PNG 水印图片合成)来说,其大致实现原理如下:


var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');

// img: 底图
// watermarkImg: 水印图片
// x, y 是画布上放置 img 的坐标
ctx.drawImage(img, x, y);
ctx.drawImage(watermarkImg, x, y);

直接连续使用

drawImage()
把对应的图片绘制到
canvas
画布上就行。

以上就是背景介绍。但是略麻烦的是添加水印的需求中还有一个需要实现的功能是用户能够切换水印的位置。我们自然会想到能否实现

canvas
undo
功能,当用户切换水印位置时,先撤销上一步
drawImage
操作,然后再重新绘制水印图片位置。

restore
/
save
?

效率最高也是最方便的肯定是查阅

canvas 2D
原生 API 是否有此功能。经过一番搜索,
restore
/
save
这一对 API 进入视线。我们先看一下这两个 API 的描述:

CanvasRenderingContext2D.restore() 是 Canvas 2D API 通过在绘图状态栈中弹出顶端的状态,将 canvas 恢复到最近的保存状态的方法。 如果没有保存状态,此方法不做任何改变。

CanvasRenderingContext2D.save() 是 Canvas 2D API 通过将当前状态放入栈中,保存 canvas 全部状态的方法。

乍看起来可以满足需求。我们看一下官方示例代码:


var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

ctx.save(); // 保存默认的状态
ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 100);

ctx.restore(); // 还原到上次保存的默认状态
ctx.fillRect(150, 75, 100, 100);

结果如下图所示:

奇怪,好像和我们预期的结果不太一致。我们想要的结果是

save
方法调用后能够保存当前画布的快照,