Canvas实现一个Tshirt制作应用

之前公司做过一个Tshirt制作的应用,我觉得这中间涉及到的东西还是挺多的,为了巩固知识,自己也做了一个,不过之前公司做的那个是基于vuejs来做的,模块化开发,结构比较清晰。而我自己的这个是用原生js写的,代码都堆在一块儿,不过相对于公司那个,我觉得在装饰图的处理方面,我用的这种方法可能会稍微好点,之前公司的那个是在Tshirt中间部位固定一个区域,装饰图只能在该区域内移动而且只在该区域内可见,我自己做的这个是在Tshirt任意位置可见并可移动,当然我这个只是简单的实现,完成了部分功能。
下面说说实现这个功能的有几个要点,布局啥的就不说了:
1、溶图
就是将两张图片融合为一张图片,并需要根据最终的效果保留一些各自的细节,在这里我使用了最简单最粗暴的方法,通过比例来计算最终的像素值。不过对于透明的区域需要有特殊的处理。
2、如何将canvas导出为图片并下载。
通过canvas的toDataURL方法可以将canvas转换为base64编码的图片,然后通过替换base64编码中的格式标示可以实现图片的下载。

var data = canvas.toDataURL("image/png");
data = data.replace("image/png","image/octet-stream");
location.href = data;

实现这个效果的完整代码:

 
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Tshirt制作</title>
	<link rel="stylesheet" href="css/style.css">
</head>
<body>

<div class="header">

<ul>

<li><a href="javascript:void(0);" id="download">保存</a></li>

		</ul>

	</div>


<div class="main">

<div class="left" id="list">

<ul>

<li><img src="images/0.png" alt=""></li>


<li><img src="images/1.png" alt=""></li>


<li><img src="images/2.png" alt=""></li>


<li class="current"><img src="images/3.png" alt=""></li>


<li><img src="images/4.png" alt=""></li>

			</ul>

		</div>


<div class="right" id="operation">
			<canvas id="canvas"></canvas>
			<img src="images/a.png" alt="" id="elem">
		</div>

	</div>


<div id="status"></div>

	<script type="text/javascript">
		var canvas,ctx,statusLabel,elem,download,list,dataArr,x,y,disX,disY,ratio,isDown,origData,origWidth,origHeight;
		function init(){
			canvas = document.getElementById("canvas");
			statusLabel = document.getElementById("status");
			operation = document.getElementById("operation");
			elem = document.getElementById("elem");
			preview = document.getElementById("preview");
			download = document.getElementById("download");
			list = document.getElementById("list");
			ctx = canvas.getContext("2d");
			origWidth = operation.offsetWidth;
			origHeight = operation.offsetHeight;
			dataArr = [];
			isDown = false;
			rePositioned = false;
			load('images/3.png','images/a.png',function(){
				statusLabel.innerText = "图片合成中,请稍后......";
				setOperationArea();
				make();
				statusLabel.innerText = "图片合成成功!";
			});
			bindEvents();
		}
		init();
		function load(tshirt,add,callback){
			statusLabel.innerHTML = "数据装载中,请稍后......";
			getImageData(tshirt,function(){ //加载Tshirt数据
				origData = copyImageData(dataArr[0]); //保留一份原始Tshirt数据
				getImageData(add,function(){ //加载装饰画数据
					callback && callback();
				},0.7) 
			},0.7)
		}
		function setOperationArea(){
			var tshirt = dataArr[0],add = dataArr[1];
			canvas.width = tshirt.width;
			canvas.height = tshirt.height;
			operation.style.width = tshirt.width + 'px';
			operation.style.height = tshirt.height + 'px';
			var marRight = (origWidth - tshirt.width) / 2;
			var marBottom = (origHeight - tshirt.height) / 2;
			operation.style.bottom = marBottom + 'px';
			operation.style.right = marRight + 'px';
			operation.style.top = "auto";
			x = (tshirt.width - add.width) / 2;
			y = Math.floor((tshirt.height - add.height) / 2);
			elem.style.left = x + 'px';
			elem.style.top = y + 'px';
			elem.width = add.width;
		}
		function getImageData(src,callback,ratio,width,height){
			var im = new Image(),
				canvas = document.createElement("canvas"),
				ctx = canvas.getContext("2d");
			im.src = src;
			im.onload = function(){
				var w = width || this.width,
					h = height || this.height;
				if(ratio){
					w = w * ratio;
					h = h * ratio;
				}
				canvas.width = w;
				canvas.height = h;
				ctx.drawImage(im,0,0,w,h);
				dataArr.push(ctx.getImageData(0,0,w,h));
				callback();
			}
		}
		function make(){
			var target = dataArr[0],
				add = dataArr[1],
				iData = copyImageData(origData);
			for(var i=y;i<iData.height;i++){ if(i>(y+add.height)) break;
				for(var j=x;j<iData.width;j++){ if(j>(x+add.width)) break;
					var index = i * iData.width * 4 + j * 4,
						r = iData.data[index],
						g = iData.data[index+1],
						b = iData.data[index+2],
						a = origData.data[index+3];
					if(a>0){
						var index1 = (i-y) * add.width * 4 + (j-x) * 4;
						if(add.data[index1+3]>30){
							var aa = add.data[index1+3] === 255 ? ratio : add.data[index1+3]/255;
							iData.data[index] = (1-aa) * r + aa * add.data[index1];
							iData.data[index+1] = (1-aa) * g + aa * add.data[index1+1];
							iData.data[index+2] = (1-aa) * b + aa * add.data[index1+2];
						}
					}
				}
			}
			ctx.putImageData(iData,0,0);
		}
		function bindEvents(){
			elem.onmousedown = mouseDown;
			elem.onmousemove = mouseMove;
			elem.onmouseout = mouseUp;
			elem.onmouseup = mouseUp;
			download.onclick = function(){
				var data = canvas.toDataURL("image/png");
				data = data.replace("image/png","image/octet-stream");
				saveFile(data,"tshirt.png");
			}
			var lis = list.getElementsByTagName("li");
			lis = Array.prototype.slice.call(lis,0);
			for(var i=0;i<lis.length;i++){
				(function(i){
					lis[i].onclick = function(){
						lis.map(function(item){
							item.className = '';
						});
						lis[i].className = 'current';
						dataArr.length = 0;
						load(lis[i].childNodes[0].src,'images/a.png',function(){
							statusLabel.innerText = "图片合成中,请稍后......";
							setOperationArea();
							make();
							statusLabel.innerText = "图片合成成功!";
						});
					}
				})(i);
			}
		}
		function saveFile(data,filename){
			var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
			save_link.href = data;
			save_link.download = filename;
			var event = document.createEvent('MouseEvents');
		    event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
		    save_link.dispatchEvent(event);
		}
		function mouseDown(){
			event.preventDefault();
			isDown = true;
			disX = event.pageX - elem.offsetLeft;
			disY = event.pageY - elem.offsetTop;
		}
		function mouseMove(){
			event.preventDefault();
			if(isDown){
				x = event.pageX - disX;
				y = event.pageY - disY;
				make();
				elem.style.left = x + 'px';
				elem.style.top = y + 'px';
			}
		}
		function mouseUp(){
			event.preventDefault();
			isDown = false;
		}
		function copyImageData(imgdata){
			return new ImageData(new Uint8ClampedArray(imgdata.data),imgdata.width,imgdata.height);
		}
	</script>
</body>
</html>

由于获取图片像素这个过程都是异步的,所以对于这个的处理要特别注意,不然很可能画不出来画面。

在线Demo:http://demo.deanhan.cn/Tshirt(鼠标拖动装饰图可以移动位置)

  • 支付宝二维码 支付宝
  • 微信二维码 微信
相关文章