使用canvas合成图片并得到的启发
来源:互联网 发布:淘宝网天猫下载 编辑:程序博客网 时间:2024/05/22 19:35
以前我也写过图片合成的简单实现,但是人是不断进步的,经过一系列的学习和研究,终于得到了较好的合成图片的方案。
图片合成本身没什么难点,但是想写好图片合成并不容易,就好像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(); } };})();