canvas 合并多张圆角图片

项目需求要求,需要多张图片动态合成一张,且每个图片都有圆角。具体效果如下所示:

tubiao.bd8ffd6.png

解决思路:先动态生成每个圆角的图片,然后把每个圆角的图片合成一张图。需要使用的方法主要是:canvas里的clip drawIamge promise 以及图片的动态js加载

  • drawImage(img,x,y,width,height),主要是用来绘制图像,也是最关键实现的代码之一。
  • clip() 主要是用来裁切圆角,先用画出一个正方形的圆角图片,然后裁切。
  • new Image() 去动态加载图片资源

第一步,先把单张的原始图处理成圆角的图片,主要代码如下:

//画出矩形圆角
function drawRoundRect(ctx,x,y,w,h,r){
    ctx.beginPath();
    ctx.arc(x+r,y+r,r,Math.PI,Math.PI*3/2);
    ctx.lineTo(w-r+x,y)
    ctx.arc(w-r+x,r+y,r,Math.PI*3/2,Math.PI*2)
    ctx.lineTo(w+x,h+y-r)
    ctx.arc(w-r+x,h-r+y,r,0,Math.PI/2)
    ctx.lineTo(r+x,h+y)
    ctx.arc(r+x,h-r+y,r,Math.PI/2,Math.PI);
    ctx.closePath();
}

//动态生成裁切图
window.onload=function(){
    var img = new Image();
	img.crossOrigin = "anonymous";
    img.src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRIIgaOFwohofaftXTSUPZmpKblrpSrlnHDLbq1y3HQlA&s";

    var canvas=document.getElementById('canvas');
    canvas.width=500;
    canvas.height=500;
    var ctx =canvas.getContext("2d");
    ctx.fillStyle="#fff";
    ctx.fillRect(0,0,500,500);
    drawRoundRect(ctx,200,200,200,200,25);
    ctx.strokeStyle="#000";
    ctx.stroke();
    ctx.clip()

    ctx.drawImage(img, 200,200,200,200);
    document.querySelector('img').src=canvas.toDataURL('image/png', 0.92)
}

复制代码

生成效果如下:

下载.png

第二步,把裁切的图片进行合并,主要代码如下:

	// 设置默认对象
	var defaultOptions = {
		format: 'image/png',
		quality: 0.92,
	};

	// 返回一个 Promise对象进行处理
	var mergeImages = function (sources, options) {
		if ( sources === void 0 ) sources = [];
		if ( options === void 0 ) options = {};

		return new Promise(function (resolve) {
		options = Object.assign({}, defaultOptions, options);

		var canvas = options.Canvas ? new options.Canvas() : window.document.createElement('canvas');
		var Image = options.Image || window.Image;

		// 图片加载的时候,去获取图片
		var images = sources.map(function (source) { return new Promise(function (resolve, reject) {
			
			if (source.constructor.name !== 'Object') {
				source = { src: source };
			}

			// 图片加载完后Resolve
			var img = new Image();
			img.crossOrigin = "anonymous";//支持跨域请求资源
			img.onerror = function () { return reject(new Error('Couldn\'t load image')); };
			img.onload = function () { return resolve(Object.assign({}, source, { img: img })); };
			img.src = source.src;
		}); });

		// 获取画布对象
		var ctx = canvas.getContext('2d');

		// 当所有图片通过js请求结束后,去在画布画出图片
		resolve(Promise.all(images)
			.then(function (images) {
				var getSize = function (dim) { return options[dim] || Math.max.apply(Math, images.map(function (image) { return image.img[dim]; })); };
				canvas.width = getSize('width');
				canvas.height = getSize('height');

				// 在画布上画图
				images.forEach(function (image) {
					ctx.globalAlpha = image.opacity ? image.opacity : 1;
					return ctx.drawImage(image.img, image.x || 0, image.y || 0,image.w||0,image.h||0);
				});

				if (options.Canvas && options.format === 'image/jpeg') {
			
					return new Promise(function (resolve, reject) {
						canvas.toDataURL(options.format, {
							quality: options.quality,
							progressive: false
						}, function (err, jpeg) {
							if (err) {
								reject(err);
								return;
							}
							resolve(jpeg);
						});
					});
				}

				// 所有异步结束后,生成合并图片
				return canvas.toDataURL(options.format, options.quality);
			}));
	});
	};
       
//调用对象生成合并图,举例如下 
    mergeImages([{src:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRIIgaOFwohofaftXTSUPZmpKblrpSrlnHDLbq1y3HQlA&s',x:0,y:0,w:100,h:100},
    {src:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRIIgaOFwohofaftXTSUPZmpKblrpSrlnHDLbq1y3HQlA&s',x:200,y:200,w:100,h:100}],{width:600,height:600})
  .then(b64 => document.querySelector('img').src = b64);        
复制代码
  <img src="" alt="">
复制代码
分类:
前端
标签: