当前位置 博文首页 > ljzc002:网页小实验——用canvas生成精灵动画图片
实验目标:借助canvas把一张国际象棋棋子图片转换为一组适用于WebGL渲染的精灵动画图片,不借助其他图片处理工具,不引用其他库只使用原生js实现。
初始图片如下:
一、图片分割
将初始图片分割为六张大小相同的棋子图片
1、html舞台:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>处理棋子图片</title> 6 </head> 7 <body> 8 <canvas id="can_source" style="z-index: 1;top:2px;left:2px;position: absolute"></canvas><!--显示原图的画布--> 9 <canvas id="can_mask" style="z-index: 10;top:2px;left:2px;position: absolute"></canvas><!--显示操作范围提示的画布--> 10 <canvas id="can_maskbak" style="z-index: 1;top:2px;left:2px;position: absolute"></canvas><!--用来划分区域的背景画布--> 11 </body> 12 <script><!--主体代码--> 13 </script> 14 </html>
这里准备了三张canvas画布,其中can_source是预览原图的画布,称为“源画布”;can_mask是悬浮在can_source上层的透明背景画布,用来绘制切割范围提示,称为“提示画布”;can_maskbak用来圈定切割范围(其实可以不显示它),称为“范围画布”。
2、分割流程:
1 var can_source=document.getElementById("can_source"); 2 var can_mask=document.getElementById("can_mask"); 3 var can_maskbak=document.getElementById("can_maskbak"); 4 var top_res; 5 var width=0,height=0; 6 window.onload=function(){ 7 var img=new Image(); 8 img.src="../../ASSETS/IMAGE/ICON/chesses.jpg"; 9 img.onload=function(){ 10 width=img.width;//根据图片尺寸设置画布尺寸 11 height=img.height; 12 can_source.style.width=width+"px";//css尺寸 13 can_source.style.height=height+"px"; 14 can_source.width=width;//canvas像素尺寸 15 can_source.height=height; 16 var con_source=can_source.getContext("2d"); 17 con_source.drawImage(img,0,0);//显示原图 18 19 top_res=height+4+"px"; 20 can_maskbak.style.left=width+4+"px";//把这个圈定范围的画布放在右边,做对比 21 can_maskbak.style.width=width+"px"; 22 can_maskbak.style.height=height+"px"; 23 can_maskbak.width=width; 24 can_maskbak.height=height; 25 var con_maskbak=can_maskbak.getContext("2d"); 26 con_maskbak.fillStyle="rgba(0,0,0,1)";//填充完全不透明的黑色 27 con_maskbak.fillRect(0,0,width,height); 28 29 can_mask.style.width=width+"px"; 30 can_mask.style.height=height+"px"; 31 can_mask.width=width; 32 can_mask.height=height; 33 var con_mask=can_mask.getContext("2d"); 34 con_mask.fillStyle="rgba(0,0,0,0)"; 35 con_mask.fillRect(0,0,width,height); 36 //下面是具体的操作代码 37 //cutRect(40,10,120,240,256,256);//矩形切割 38 //cutRect(192,10,120,240,256,256); 39 //cutRect(340,10,120,240,256,256); 40 cutRect(33,241,120,240,256,256); 41 cutRect(200,241,120,240,256,256); 42 cutRect(353,241,120,240,256,256); 43 } 44 }
3、具体切割算法:
1 //从一个画布上下文中剪切一块dataUrl 2 function cutRect(x,y,wid,hig,wid2,hig2) 3 { 4 //将矩形转换为路径,然后用更一般化的路径方法处理区域 5 var path=[{x:x,y:y},{x:x+wid,y:y},{x:x+wid,y:y+hig},{x:x,y:y+hig}]; 6 var framearea=[x,y,wid,hig];//framearea是操作范围的边界,矩形切割则直接是矩形本身,多边形切割则应是多边形的外切矩形范围 7 cutPath(path,framearea,wid2,hig2); 8 9 } 10 function cutPath(path,framearea,wid2,hig2) 11 { 12 var len=path.length; 13 var con_mask=can_mask.getContext("2d"); 14 con_mask.strokeStyle="rgba(160,197,232,1)";//线框 15 con_mask.beginPath(); 16 for(var i=0;i<len;i++) 17 { 18 var point=path[i]; 19 if(i==0) 20 { 21 con_mask.moveTo(point.x,point.y); 22 } 23 else { 24 con_mask.lineTo(point.x,point.y); 25 } 26 27 } 28 con_mask.closePath();//在提示画布中绘制提示框 29 con_mask.stroke(); 30 //con_mask.Path; 31 32 33 var con_maskbak=can_maskbak.getContext("2d"); 34 con_maskbak.beginPath(); 35 con_maskbak.fillStyle="rgba(0,255,0,1)"; 36 con_maskbak.lineWidth=0; 37 for(var i=0;i<len;i++) 38 { 39 var point=path[i]; 40 con_maskbak.lineTo(point.x,point.y); 41 } 42 con_maskbak.closePath(); 43 con_maskbak.fill();//在范围画布中画出切割的范围(纯绿色) 44 45 var con_source=can_source.getContext("2d"); 46 var data_source=con_source.getImageData(framearea[0],framearea[1],framearea[2],framearea[3]);//获取源画布在操作范围内的像素 47 var data_maskbak=con_maskbak.getImageData(framearea[0],framearea[1],framearea[2],framearea[3]);//获取范围画布在操作范围内的像素 48 49 var can_temp=document.createElement("canvas");//建立一个暂存canvas作为工具,并不实际显示它。 50 can_temp.width=wid2||framearea[2];//设置暂存画布的尺寸,这里要把长方形的切图保存为正方形! 51 can_temp.height=hig2||framearea[3]; 52 var con_temp=can_temp.getContext("2d"); 53 con_temp.fillStyle="rgba(255,255,255,1)"; 54 con_temp.fillRect(0,0,can_temp.width,can_temp.height); 55 var data_res=con_temp.createImageData(framearea[2],framearea[3]);//建立暂存画布大小的像素数据 56 57 58 var len=data_maskbak.data.length; 59 for(var i=0;i<len;i+=4)//对于范围画布的每一个像素 60 { 61 if(data_maskbak.data[i+1]=255)//如果这个像素是绿色 62 { 63 data_res.data[i]=(data_source.data[i]);//则填充源画布的对应像素 64 data_res.data[i+1]=(data_source.data[i+1]); 65 data_res.data[i+2]=(data_source.data[i+2]); 66 data_res.data[i+3]=(data_source.data[i+3]); 67 } 68 else 69 { 70 data_res.data[i]=(255);//否则填充完全不透明的白色,注意不透明度通道在rgba表示中是0到1,在data表示中是0到255! 71 data_res.data[i+1]=(255); 72 data_res.data[i+2]=(255); 73 data_res.data[i+3]=(255); 74 } 75 } 76 con_temp.putImageData(data_res,(can_temp.width-framearea[2])/2,(can_temp.height-framearea[3])/2)//把填充完毕的像素数据放置在暂存画布的中间 77 console.log(can_temp.toDataURL());//以dataUrl方式输出暂存画布的数据 78 79 }
4、切割效果如下:
在控制台里可以找到以文本方式输出的图片数据:
对于小于2MB的图片数据,直接复制dataUrl粘贴到浏览器地址栏回车,即可显示完整图片,之后右键保存;对于大于2MB的图片数据则需把can_temp显示出来,之后右键保存。精灵动画的单帧图片一般较小,所以不考虑需要显示can_temp的情况。
最终获取的一张“兵”图片:
5、改进
其实canvas的path对象本身就有clip方法,可以用这个内置方法简化以上过程。
clip方法的文档:https://www.w3school.com.cn/tags/canvas_clip.asp
二、生成精灵动画
1、html舞台及准备代码:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>建立棋子的动画帧,添加一个图标样式</title> 6 </head> 7 <body> 8 <canvas id="can_back" style="z-index: 1;top:2px;left:2px;position: absolute"></canvas><!--徽章的背景--> 9 <canvas id="can_back2" style="z-index: 1;top:2px;left:2px;position: absolute"></canvas> 10 <canvas id="can_res" style="z-index: 1;top:2px;left:2px;position: absolute"></canvas><!--显示结果--> 11 </body> 12 <script> 13 var can_back=document.getElementById("can_back"); 14 var can_back2=document.getElementById(