码字,杂谈

JavaScript异步加载图片

之前写的画板里面,我将它升级了一下,首先可以传入一张默认图片,然后所有操作都是基于该图片进行操作。然后我发现,当使用橡皮擦的时候,它直接将整个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");
});

就这样,不仅代码简单很多,而且整洁干净,思路也清晰了很多。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注