之前写的画板里面,我将它升级了一下,首先可以传入一张默认图片,然后所有操作都是基于该图片进行操作。然后我发现,当使用橡皮擦的时候,它直接将整个canvas擦成了透明。
这是因为canvas每次只能展示一张图片,这个在之前说过,有兴趣的朋友可以参考之前的文章。
于是有了很简单的想法,在擦除完成后,首先在canvas中加载原始图片,然后加载擦除后的图片,这样重叠合并成一张完整的擦除后的效果图。
有了想法,动手做:
// 首先保存擦除的图片
let eraserPic = new Image();
eraserPic.src = this.canvasElem.toDataURL("image/png");
// 加载原始图片
let originPic = new Image();
originPic.src = this.originImage;
// 对原始图片响应,这里需要使用`load`响应,否则会有神奇的效果。。。
originPic.addEventListener("load", () => {
this.ctx.drawImage(originPic, 0, 0, this.canvasElem.width, this.canvasElem.height);
// 先加载原始图,然后加载擦除后的图片,这样原始图片在下,擦除图片在上面,进行合并
eraserPic.addEventListener("load", () => {
this.ctx.drawImage(eraserPic, 0, 0, this.canvasElem.width, this.canvasElem.height);
// 保存合并后的图片
this.capturePic = this.canvasElem.toDataURL("image/png");
// 将新图片添加到undo区域,方便后续操作
this.undoPushHandle();
});
});
然后发现真实可用,但是发现总会有问题,时不常一擦除就清空,只显示原始图。多次检查后,感觉是load
事件的加载问题,图片加载是一个等待过程,但是我们的代码操作的很快,这样就会出现没有监听到,代码认为不需要load,也就不会加载load
里面的事件。
一开始我把所有addEventListener
都写在了一层,发现问题后,套了进去,还是有小概率机会出现不加载的问题,于是想到了Promise
。
使用异步加载图片
异步的内容很简单,封装一个新建Image的函数,让所有图片都加载好之后返回即可。这样就可以完全等待图片加载成功后执行下一步。
newImageProcess: function(src) {
return new Promise((resolve, reject) => {
let img = new Image();
// 因为有些地方获取图片需要使用跨域截图,所有添加到这里
img.setAttribute("crossOrigin", "anonymous");
img.onload = () => resolve(img);
img.onerror = reject;
// src赋值在最后,这样做更加保险可以调用`load`事件。
img.src = src;
})
}
有了这段代码,后面的操作就行云流水般的简单明了了。
this.newImageProcess(this.canvasElem.toDataURL("image/png")).then((result) => {
// 首先加载擦除后的图片,保存为变量
let eraserPic = result;
this.newImageProcess(this.originImage).then((result) => {
// 然后加载原始图片,同样保存为变量
let originPic = result;
// 依次在canvas中加载原始图片和擦除后图片,使其合并为一张最终效果图
this.ctx.drawImage(originPic, 0, 0, this.canvasElem.width, this.canvasElem.height);
this.ctx.drawImage(eraserPic, 0, 0, this.canvasElem.width, this.canvasElem.height);
// 保存图片并添加到undo操作区域
this.videoCapture = this.canvasElem.toDataURL("image/png");
this.undoPushHandle();
}).catch((err) => {
console.log("origin error");
});
}).catch((err) => {
console.log("eraser error");
});
就这样,不仅代码简单很多,而且整洁干净,思路也清晰了很多。
文章评论