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");
});

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章