原文位址: 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");
});
就這樣,不僅代碼簡單很多,而且整潔幹淨,思路也清晰了很多。