天天看點

JavaScript異步加載圖檔

原文位址: https://www.jeremyjone.com/526/ ,轉載請注明

之前寫的畫闆裡面,我将它更新了一下,首先可以傳入一張預設圖檔,然後所有操作都是基于該圖檔進行操作。然後我發現,當使用橡皮擦的時候,它直接将整個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");
});
           

就這樣,不僅代碼簡單很多,而且整潔幹淨,思路也清晰了很多。

繼續閱讀