首先我们知道css3中增加了不少好用、好玩的css3样式可以使用。今天我们要说到是遮罩。
它的使用方式也不复杂,和background使用方式差不多。使用mask-image就可以使用,这样就可以通过图片合成一张带有形状的合成图了,不需要直接使用PS处理了。CSS遮罩——如何在CSS中使用遮罩,这篇文章已经详细说明了遮罩如何使用了,我这里就不赘述了。今天我这里想要说明的是,如何通过这个完成生成一个合成图片的逻辑。
它通过两种方式:前端JS+canvas,后端nodejs+canvas,完成基本思路是一样的,但是两种方式,后者的兼容性稍好一些。
基础的图片合成,整体的思路是:首先按照遮罩层的大小,生成一张图片;其次把原图片按照一定的规则按照遮罩层大小剪切(drawImage)一张图片然后合成到遮罩层上面;最后一个非常重要设置是:globalCompositeOperation,设置图片合成时,组合操作。
前端JS实现
/** * 生成canvas遮罩(由于在安卓手机上的兼容问题,未使用,采用服务端接口产生) * @param imgList */ utils.canvasMasking = function(imgList) { var _imgAddMask = function(img) { var deferred = $.Deferred(); var newImg = document.createElement(‘img‘); newImg.setAttribute(‘crossOrigin‘, ‘Anonymous‘); //解决跨域问题 newImg.src = img.src; //源图片加载失败 newImg.onerror = function() { deferred.reject(‘源图片加载失败‘); }; //源图片加载成功 newImg.onload = function() { var imageWidth = img.width; var imageHeight = img.height; var mask = document.createElement(‘img‘); mask.setAttribute(‘crossOrigin‘, ‘Anonymous‘); mask.src = img.getAttribute(‘data-mask‘); //遮罩图片加载失败 mask.onerror = function() { deferred.reject(‘遮罩图片加载失败‘); }; //遮罩图片加载成功 mask.onload = function() { var maskCanvas = document.createElement(‘canvas‘); var maskContext = maskCanvas.getContext(‘2d‘); var imageCanvas = document.createElement(‘canvas‘); var imageContext = imageCanvas.getContext(‘2d‘); var maskWidth = $(img).parents(‘.mask-wrapper‘).width(); var maskHeight = $(img).parents(‘.mask-wrapper‘).height(); maskCanvas.width = maskWidth; maskCanvas.height = maskHeight; var _x = $(img).data(‘x‘) + ‘‘; var _y = $(img).data(‘y‘) + ‘‘; var x = Math.abs(_x.indexOf(‘%‘) > -1 ? imageWidth * parseFloat(_x)/100 : parseFloat(_x)); var y = Math.abs(_y.indexOf(‘%‘) > -1 ? imageHeight * parseFloat(_y)/100 : parseFloat(_y)); var scale = parseFloat($(img).data(‘scale‘)); var angle = parseFloat($(img).data(‘angle‘)); imageCanvas.width = imageWidth * scale; imageCanvas.height = imageHeight * scale; /** * 处理原始图片旋转、缩放 */ //移动到图片中心点,与css中心点保持一致 imageContext.translate(imageCanvas.width/2, imageCanvas.height/2); imageContext.scale(scale, scale); imageContext.rotate(angle); //恢复canvas中心点到做顶点 imageContext.translate(-imageCanvas.width/2, -imageCanvas.height/2); imageContext.drawImage(img, 0, 0, imageCanvas.width, imageCanvas.height); /** * 合并mask与处理后的原始图 */ maskContext.drawImage(mask, 0, 0, maskWidth, maskHeight); //将一个源(新的)图像绘制到目标(已有)的图像上 maskContext.globalCompositeOperation = ‘source-in‘; maskContext.drawImage(imageCanvas, x, y, maskWidth, maskHeight, 0, 0, maskWidth, maskHeight); img.src = maskCanvas.toDataURL(); $(‘body‘).prepend(imageCanvas, maskCanvas); $(img).siblings(‘.mask‘).hide(); deferred.resolve(maskCanvas); }; }; return deferred.promise(); };
由于canvas是禁止跨域的,所以有两个条件可以进行控制:
- 图片的头信息必须设置允许跨域的头(Access-Control-Allow-Origin:*)
- 创建的image标签必须也能允许跨域(img.setAttribute(‘crossOrigin’, ‘Anonymous‘))
大概实现过程如下:
后端node实现
后端实现的大致思路和前端js操作canvas基本上差不多。后端需要依赖node-canvas模块,提供与浏览器几乎完全一致的api,所以可以无缝切换。详细的实现说明同上,这里就不详细说明,主要说明的可能是node-canvas的安装和正常使用。
因为是处理图片,所以对安装的环境有一些要求,相对比其它的模块要求稍高一些。首先他依赖Cairo。有一些依赖关系需要安装,主要是处理图片的库,看具体需求,主要有处理三种图片:png、jpeg、gif。
OS | Command |
---|---|
OS X | brew install pkg-config cairo libpng jpeg giflib |
Ubuntu | sudo apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++ |
Fedora | sudo yum install cairo cairo-devel cairomm-devel libjpeg-turbo-devel pango pango-devel pangomm pangomm-devel giflib-devel |
Solaris | pkgin install cairo pkg-config xproto renderproto kbproto xextproto |
Windows | Instructions on our wiki |
上表的信息来自github。
这里我说明一下我踩过的坑:
- 第一次由于没有安装jpeg导致处理jpg图片时,出现加载失败的提示,遇到这种情况可以检查自己的库依赖是不是没有安装完全;
- 第二次安装了jpeg的库之后,发现依然不行,最后发现问题在于我的jpeg的库是在我已经安装完成node-canvas之后才安装的
- 第三次把在mac安装好的node-modules复制到centos的服务器上,发现canvas不可以用。原因是他们的图片处理库不一样,必须重新安装node-canvas
综上所述:安装node-canvas的前提条件是安装所有依赖的库文件,不同的环境下,需要重新下载node-canvas进行安装,原因是node-canvas每次都要编译一遍。
另外一些题外的问题:
- 合成图片的时候,如果需要移动、缩放或者旋转怎么处理?可以使用使用canvas的tranlate、scale、rotate进行完成,这里需要注意的是网页上的呈现效果和合成处理是否一致的问题。
- 前端合成多张遮罩图片的时候,不同的手机的兼容性问题
- 微信里的页面无法通过下载按钮,下载生成的图片;并且canvas.otDataURL()的图片无法长按保存
- 使用html2canvas将dom生成图片的时候,需要设置允许跨域,如果要使用proxy的话,可以参考进行实现。
- 另外html2canvas生成的canvas信息在上传base64的时候,可能出现413,body实体太大的提示,这时候需要检查服务器或者服务器语言的允许body的大小是否有限制。
我目前遇到一个问题:浏览器不同分辨率下展现的大小和我服务端生成的图片不一致?正在解决。。。
参考资料:
https://github.com/niklasvh/html2canvas
https://github.com/Automattic/node-canvas
https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D