使用canvas合成圖片並得到的啟發
以前我也寫過圖片合成的簡單實現,但是人是不斷進步的,經過一系列的學習和研究,終於得到了較好的合成圖片的方案。
圖片合成本身沒什麼難點,但是想寫好圖片合成並不容易,就好像js入門容易,但是想成為大神難。
言歸正傳:
我的這個版本的程式碼不僅是圖片合成,而且還有模組化程式設計,圖片載入,回撥函式等知識點。
首先要講的是圖片載入,為什麼先將圖片載入,這是因為沒有圖片的載入,你就沒辦法完成圖片的合成。
你如果自己寫過圖片操作的程式碼,就會明白程式碼執行在圖片的載入完成之前,所以想要操作圖片第一必須確保圖片的載入完成後再進行圖片的操作,所以有兩種方法(本人的方法)可以實現這項功能,定時器函式和回撥函式。
第一種使用回撥函式:
推薦使用setInterval(),你需要先在外部定義一個標識,這個標識可以判斷圖片是否載入完成,例如我的程式碼中imagesLoaded。為什麼可以用,首先你的程式碼比圖片執行的快,但是定時卻可以每隔一段時間就會執行一次,一直等到圖片載入完成,再停止定時器,這是一種笨方法,但是邏輯簡單。
第二種使用回撥函式:
推薦這種方式,因為回撥本來就是用來執行非同步呼叫的,特別是onload事件,是在所有圖片載入完成後執行的,這是一個很清楚的時機,所以使用callback函式可以簡單方便的等待圖片載入完成後再進行圖片合成,但是如果你回撥的函式中有this變數的話,你需要了解this的指向,例如我的程式碼中如果我不將this賦值給_this,那麼imagesMerged 函式中的this指向的是handleLoadedImage 而不是MergeImages的原型物件,所以你必須特別注意這一點。
圖片的載入必須是有序載入,否則圖片合成的順序會發生錯誤。
其次要講的是模組化,為什麼要模組話,當你的js檔案足夠多,足夠大,難免有同名變數和同名函式,這時候,就會互相影響,產生極大的隱患,
js本身有名稱空間,但是使用起來太過繁瑣,所以推薦使用模組,不僅便於管理,而且便於使用,例如如果想使用我的程式碼,你只需要引入我的MergeImagePlain.js,在你的html檔案有一個id=”showMergedImage”的img,最後在你的js檔案中呼叫即可,例如我在自己的js檔案中呼叫:
var imgs = ['imgs/icon3.png', 'imgs/icon1.png', 'imgs/icon2.png', 'imgs/sword.png' ];
mergeImagesModule.mergeImages(imgs);
合成的圖片的:
MergeImagePlain.js:
//將合成圖片的功能函式合併為合成圖片模組
var mergeImagesModule = (function () {
//宣告全域性變數
var imgs_w = []; //儲存全部圖片的寬度
var imgs_h = []; //儲存全部圖片的高度
var imgObjs = []; //儲存全部圖片的物件
// var mergedImageSrc = ''; //儲存合成後圖片的src
// var imagesLoaded = false; //用於判斷圖片是否全部載入完畢
/*
*定義一個名為formatArray的函式,用於將任意型別的變數轉換為陣列變數
*@param Everytype param 任意型別的變數
*@return array 轉換後的陣列變數
*/
function formatArray (param) {
if (!param) {
return [];
}
if (Array.isArray(param)) {
return param;
}
if (Array.from) {
return Array.from(param);
}
return [param];
}
/*
*定義一個名為getArrayMaxValue的函式,用於獲取陣列變數的最大值
*@param Everytype elems 任意型別的變數
*@return maxVlue 陣列中的最大值
*/
function getArrayMaxValue (elems) {
elems = formatArray(elems);
return Math.max.apply(null, elems);
}
/*
*定義一個名為MergeImages的函式,作為合成圖片的例項原型
*@param array images 圖片連線組成的陣列
*@param string type 合成圖片的型別
*@return void
*/
function MergeImages (images, type) {
try {
if (!images) {
throw 'MergeImages的images引數不能為空';
}
this.images = formatArray(images);
if (type && 'string'===typeof(type)) {
this.type = type;
} else {
this.type = "image/png";
}
} catch (err) {
console.log('Error:', err);
}
}
/*
*定義一個名為getImagesRealSizes的函式,作為合成圖片的例項原型的函式,獲取圖片的資訊
*@param void
*@return void
*/
MergeImages.prototype.getImagesRealSize = function (callback) {
var _imgs = this.images,
imgs_len = _imgs.length;
var _this = this;
var count = 0;
//定義一個內部函式對圖片進行有序載入
function orderedProloadImages () {
var imgObj = new Image();
imgObj.onload = handleLoadedImage;
imgObj.onerror = handleLoadedImage;
//定義一個函式作為圖片的onload和onerror的事件函式
function handleLoadedImage () {
//儲存圖片的寬和高
imgs_w.push(this.width);
imgs_h.push(this.height);
imgObjs.push(this);
count = Math.min(imgs_len, ++count);
//判斷圖片是否載入完成
if (count < imgs_len) {
orderedProloadImages();
} else {
if ('function'===typeof(callback)) {
callback.apply(_this);
}
}
}
imgObj.src = _imgs[count];
imgObj = null;
}
orderedProloadImages();
}
/*
*定義一個名為imagesMerged的函式,作為合成圖片的例項原型的函式,用於合成圖片
*@param void
*@return string url 合成圖片的連線
*/
MergeImages.prototype.imagesMerged = function () {
var imgs_len = this.images.length,
maxWidth = 0,
maxHeight = 0,
temp_w = 0,
temp_h = 0,
offset_w = 0,
offset_h = 0,
canvas = null,
cxt = null,
url = '',
srcType = this.type;
try{
//建立畫布並設定畫布的屬性
canvas = document.createElement('canvas');
maxWidth = getArrayMaxValue(imgs_w);
maxHeight = getArrayMaxValue(imgs_h);
canvas.width = maxWidth;
canvas.height = maxHeight;
cxt = canvas.getContext('2d');
for (var i = 0; i < imgs_len; i++) {
//獲取每張圖片的寬高
temp_w = imgs_w[i];
temp_h = imgs_h[i];
//獲取每張圖片移動到畫布中心需要移動的距離(top和left)
offset_w = Math.abs(maxWidth - temp_w) * 0.5;
offset_h = Math.abs(maxHeight - temp_h) * 0.5;
//在畫布上一次將每張圖片繪製到畫布中
cxt.drawImage(imgObjs[i], 0, 0, temp_w, temp_h, offset_w, offset_h, temp_w, temp_h);
}
url = canvas.toDataURL(srcType);
cxt = null;
canvas = null;
var imgObj = document.getElementById("showMergedImage");
imgObj.src = url;
// return url;
} catch (err) {
console.log('error:', err);
}
}
/*
*定義一個名為init的函式,作為合成圖片的例項原型的函式,用於呼叫原型物件上的函式合成圖片
*@param void
*@return void
*/
MergeImages.prototype.init = function () {
try {
this.getImagesRealSize(this.imagesMerged);
// var imgObj = document.getElementById("showMergedImage");
// console.log(this.getImagesRealSize(this.imagesMerged));
// imgObj.src = this.getImagesRealSize(this.imagesMerged);
//var _this = this;
//使用定時器等待圖片載入完成併合成圖片,將圖片的連線傳給一個特定的img元素
// var timer = setInterval(function () {
// if (imagesLoaded) {
// mergedImageSrc = _this.imagesMerged();
// var imgObj = document.getElementById("showMergedImage");
// imgObj.src = mergedImageSrc;
// clearInterval(timer);
// imgObj = null;
// }
// }, 400);
} catch (err) {
console.log('error:', err);
}
}
return {
mergeImages: function (images, type) {
var mergeImages = new MergeImages(images, type);
mergeImages.init();
}
};
})();