JS | canvas 繪製多圖
“處理圖片是我們普通市民的責任,積累跬步是我本身的興趣,所以載入圖片我每次都跑一次,如果遇到多張圖片的話我還會多跑幾次。”
——題記,改自《破壞之王》
正文
canvas一籮筐問題, 之前一文 JS | canvas 圖片模糊 講了合成圖的模糊,本文講講另一個,canvas繪製多圖,這也是在做H5的過程中遇到的一個坎,比如說合成圖少元素,比如說跨域等。
原始做法:
前提多張圖片,本地開發的時候,通過改變HTML img元素的路徑src
(多個img元素在H5已有),進行繪製drawImage
,比如說drawImage(document.getElementById('img1'),
0, 0)
crossOrigin
,允許跨域請求資源。這個可以一試。
使用其它域名下的圖片
在HTMLImageElement上使用crossOrigin屬性,你可以請求載入其它域名上的圖片。如果圖片的伺服器允許跨域訪問這個圖片,那麼你可以使用這個圖片而不汙染canvas,否則,使用這個圖片將會汙染canvas。什麼是“被汙染”的 canvas?
儘管不通過 CORS 就可以在畫布中使用圖片,但是這會汙染畫布。一旦畫布被汙染,你就無法讀取其資料。例如,你不能再使用畫布的 toBlob(), toDataURL() 或 getImageData()方法,呼叫它們會丟擲安全錯誤。
這種機制可以避免未經許可拉取遠端網站資訊而導致的使用者隱私洩露。
[引自MDN]
最後方案(不在H5顯示img元素,從零建立):
通過js建立image物件,對於多張圖片,建立陣列,載入完成多張圖片後,進行canvas繪製,後再進行合成圖片。這裡有個強調,必須要在載入完成後,方可繪製,也就是說有先後順序,順序沒處理好,可能導致合成的圖片缺頭少尾,甚至空白。這裡利用jquery的deffered來處理先後順序問題。
若呼叫 drawImage時,圖片沒載入完,那什麼都不會發生(在一些舊的瀏覽器中可能會丟擲異常)。
程式碼如下:
//預載入
function preloadImg(list, imgs) {
var def = $.Deferred(),
len = list.length;
$(list).each(function (i,e) {
var img = new Image();
img.crossOrigin = ""; //跨域請求資源
img.onload = (function(j) {
return function() {
imgs[j] = img
len--;
if(len == 0) { def.resolve(); }
};
})(i);
img.onerror = function() {
len--;
alert('fail to load image');
};
img.src = e; // 確保快取的圖片也觸發 load 事件
});
console.log(def.promise())
return def.promise();
}
$.when(preloadImg(list, imgs)).done(
function() {
draw事件
})