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