引言
对canvas中绘制的图片进行旋转操作,需要使用ctx.translate变换坐标系,将图片旋转的基点设为坐标系的原点,然后ctx.rotate旋转。
这个时候,因为canvas坐标系发生了旋转,而视觉感受上的坐标以及鼠标事件中的坐标都是旋转之前的屏幕坐标系。再根据鼠标的移动去控制canvas中的图片时,就会出现问题。
用A坐标系中的偏移来控制B坐标系中的图形,就需要进行一个坐标转换,即通过一种转换关系,将A坐标系中的点在B坐标系中表示出来,然后根据B坐标系中的偏移来控制B坐标系中的图形。
下面按照先易后难的顺序,把基本的坐标转换解释一下。
[备注]
这篇文章只是记录分享下解决问题的过程,找我要demo的,或者问我什么东西怎么做的,就不要加我了。你可以加一个canvas相关的交流群,或者如果需要用到KineticJS/FabricJS的话,可以加群251572039。
一、拖拽中的坐标转换
因为没有旋转,所以不需要考虑角度变化,屏幕坐标系的偏移=canvas坐标系的偏移。
实现思路:绘制图片之前,将canvas坐标系的原点移动到图片的中心点位置。移动的时候,根据鼠标move后在屏幕坐标系的偏移得出图片中心点需要的偏移量,算出新的图片中心点的坐标,再根据新的图片中心点在屏幕坐标系的坐标计算其在canvas坐标系的坐标值P,然后将canvas坐标系的原点ctx.translate到P。
demo中有详细的注释 链接http://youryida.duapp.com/demo_canvas/coor_convert_move.html
1 <!doctype html> 2 <html> 3 <head> 4 <title> </title> 5 <meta http-equiv="X-UA-Compatible" content="IE=9"> 6 <meta charset="utf-8" /> 7 <meta http-equiv="pragma" content="no-cache"> 8 <meta http-equiv="cache-control" content="no-cache"> 9 <meta http-equiv="expires" content="0"> 10 <style> 11 #canvas{border:1px solid #ccc;} 12 </style> 13 </head> 14 <body> 15 <canvas id="canvas" width="500" height="300"></canvas> 16 <pre> 17 功能:拖拽 18 思路:始终保持图片中心点在canvas坐标系的原点处,图片的每一次重绘都基于canvas坐标系的原点来绘制,即drawImage(img,-imgW/2,-imgH/2)。 19 移动的时候绘制方法不变,变换的是canvas坐标系。 20 关键:理解屏幕坐标系和canvas坐标系的关系。将鼠标事件的屏幕坐标,转换为canvas坐标系中的坐标。 21 </pre> 22 <script> 23 var cvs =document.getElementById("canvas"); 24 var ctx =cvs.getContext("2d"); 25 var cvsH=cvs.height; 26 var cvsW=cvs.width; 27 var beginX,beginY; 28 var LT={x:30,y:30};//图片左上角的点 29 var isDown=false; 30 var imgH,imgW; 31 var moveAble=false; 32 var img = new Image(); 33 img.src ="img/niuniu.jpg"; 34 img.onload=function (){ 35 imgH=img.height; 36 imgW=img.width; 37 PO={x:LT.x+imgW/2,y:LT.y+imgH/2}; 38 ctx.translate(PO.x,PO.y); 39 onDraw(); 40 } 41 function onDraw(){ 42 ctx.clearRect(-cvsW,-cvsH,2*cvsW,2*cvsH); 43 ctx.drawImage(img,-imgW/2,-imgH/2); 44 } 45 46 cvs.addEventListener("mousedown", startMove, false); 47 cvs.addEventListener("mousemove", moving, false); 48 cvs.addEventListener("mouseup", endMove, false); 49 cvs.addEventListener("mouseout",endMove, false); 50 51 function imgIsDown(x,y){ 52 return (-imgW/2<=x && x<=imgW/2 && -imgH/2<y && y<=imgH/2); 53 } 54 55 function startMove(){ 56 event.preventDefault(); 57 isDown=true; 58 var loc=getEvtLoc();//获取鼠标事件在屏幕坐标系的位置(原点在canvas左上角) 59 var x=loc.x,y=loc.y; 60 var cLoc=convertCoor(loc); 61 var Xc=cLoc.x,Yc=cLoc.y; 62 beginX=x,beginY=y; 63 moveAble=imgIsDown(Xc,Yc); 64 if (moveAble) cvs.style.cursor="move"; 65 66 } 67 function moving(){ 68 event.preventDefault(); 69 if(isDown==false) return; 70 var loc=getEvtLoc(); 71 72 if(moveAble){ 73 var x=loc.x,y=loc.y; 74 var dx=x-beginX,dy=y-beginY; 75 var mPO={x:PO.x+dx,y:PO.y+dy};//因为鼠标移动dx dy,所以PO在屏幕坐标系的坐标也 移动dx dy 76 var cPO=convertCoor(mPO);//屏幕坐标系移动后的PO转换成canvas坐标系的坐标 77 ctx.translate(cPO.x,cPO.y);//canvas坐标系原点移动到新的图片中心点 78 onDraw(); 79 80 PO.x=PO.x+dx;//记录下屏幕坐标系上PO的坐标变化 81 PO.y=PO.y+dy; 82 beginX=x,beginY=y; //记录移动后鼠标在屏幕坐标系的新位置 83 } 84 } 85 function endMove(){ 86 event.preventDefault(); 87 isDown=false; 88 moveAble=false; 89 cvs.style.cursor="auto"; 90 } 91 function getEvtLoc(){//获取相对canvas标签左上角的鼠标事件坐标 92 return {x:event.offsetX,y:event.offsetY} 93 } 94 95 function convertCoor(P) {//坐标变换 屏幕坐标系的点 转换为canvas新坐标系的点 96 var x=P.x-PO.x;//在屏幕坐标系中,鼠标位置和新坐标系原点PO的偏移 97 var y=P.y-PO.y; 98 return {x:x,y:y}; 99 } 100 </script> 101 </body> 102 </html>