canvas坐标转换屏幕坐标_canvas 图片拖拽旋转之一——坐标转换translate

本文详细介绍了如何在canvas上实现图片的拖拽和旋转功能,重点在于坐标转换。通过将canvas坐标系原点移动到图片中心,结合鼠标事件的屏幕坐标转换,实现了拖拽。在旋转时,利用坐标变换和角度计算,实现了图片随鼠标旋转。文章还提供了拖拽和拖拽+旋转两种情况的坐标转换方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

对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。

1

2

3

4

5

6

7

8

9

10

11 #canvas{border:1px solid #ccc;}

12

13

14

15

16

 
 

17 功能:拖拽18 思路:始终保持图片中心点在canvas坐标系的原点处,图片的每一次重绘都基于canvas坐标系的原点来绘制,即drawImage(img,-imgW/2,-imgH/2)。19 移动的时候绘制方法不变,变换的是canvas坐标系。20 关键:理解屏幕坐标系和canvas坐标系的关系。将鼠标事件的屏幕坐标,转换为canvas坐标系中的坐标。21

22

23 varcvs=document.getElementById("canvas");24 varctx=cvs.getContext("2d");25 varcvsH=cvs.height;26 varcvsW=cvs.width;27 varbeginX,beginY;28 varLT={x:30,y:30};//图片左上角的点

29 varisDown=false;30 varimgH,imgW;31 varmoveAble=false;32 varimg= newImage();33 img.src="img/niuniu.jpg";34 img.οnlοad=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 functiononDraw(){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 functionimgIsDown(x,y){52 return(-imgW/2<=x && x<=imgW/2 && -imgH/2

55 functionstartMove(){56 event.preventDefault();57 isDown=true;58 varloc=getEvtLoc();//获取鼠标事件在屏幕坐标系的位置(原点在canvas左上角)

59 varx=loc.x,y=loc.y;60 varcLoc=convertCoor(loc);61 varXc=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 functionmoving(){68 event.preventDefault();69 if(isDown==false)return;70 varloc=getEvtLoc();71

72 if(moveAble){73 varx=loc.x,y=loc.y;74 vardx=x-beginX,dy=y-beginY;75 varmPO={x:PO.x+dx,y:PO.y+dy};//因为鼠标移动dx dy,所以PO在屏幕坐标系的坐标也 移动dx dy

76 varcPO=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 functionendMove(){86 event.preventDefault();87 isDown=false;88 moveAble=false;89 cvs.style.cursor="auto";90 }91 functiongetEvtLoc(){//获取相对canvas标签左上角的鼠标事件坐标

92 return{x:event.offsetX,y:event.offsetY}93 }94

95 functionconvertCoor(P) {//坐标变换 屏幕坐标系的点 转换为canvas新坐标系的点

96 varx=P.x-PO.x;//在屏幕坐标系中,鼠标位置和新坐标系原点PO的偏移

97 vary=P.y-PO.y;98 return{x:x,y:y};99 }100

101

102

图片拖拽 坐标转换demo

二、拖拽+旋转 中的坐标转换

实现思路:还是上面的思路,要把屏幕坐标系的点都转换成canvas坐标系的点。关于旋转,图片中心点不动,即canvas坐标系原点不动,鼠标摁住旋钮(假设旋钮在图片中心上方)后,图片跟随鼠标进行旋转,需要计算鼠标点在canvas坐标系中的坐标值,并且计算出该点相对canvas坐标系y轴反方向的夹角θ,然后旋转canvas坐标系ctx.rotate(θ);

带有旋转的坐标转换详解:

如左图,鼠标事件中获取到的点(M) 坐标都是基于屏幕的坐标系,即XOY坐标系。

设canvas中经过一些旋转操作之后的canvas坐标系为X'O'Y'。

因为绘图代码是依据canvas中的坐标系进行绘制,所以就需要将屏幕坐标系中点的坐标值转换成canvas坐标系中点的坐标值。

该坐标转换抽象为一道高中几何题就是:

平面内一个直角坐标系XOY,经过平移、顺时针旋转θ角度后形成新的直角坐标系X'O'Y',已知O'在XOY坐标系中的坐标为(Xo,Yo),点M在XOY坐标系中的坐标为(Xm,Ym),求M在X'O'Y'坐标系中的坐标(x',y')。

解:

如左图,从M点对两坐标系的xy轴做垂线并连接O'M,

Δx=Xm-Xo;

Δy=Ym-Yo;

O'M = Math.sqrt(Δx*Δx+Δy*Δy);//勾股定理

Math.atan2(Δy,Δx)=α+β;//M点与X轴的夹角 三角函数对边/临边

β=Math.atan2(Δy,Δx)-θ;//因为θ=α

x'=O'M*Math.cos(β);

y'=O'M*Math.sin(β); //可得M在X'O'Y'坐标系中的坐标(x',y')

over;

1

2

3

4

5

6

7

8

9

10

11 #canvas{border:1px solid #ccc;}

12

13

14

15

16

17

 
 

18 功能:拖拽+旋转19 思路:始终保持图片中心点在canvas坐标系的原点处,图片的每一次重绘都基于canvas坐标系的原点来绘制,即drawImage(img,-imgW/2,-imgH/2)。20 移动、旋转的时候绘制方法不变,变换的是canvas坐标系。21 关键:理解屏幕坐标系和canvas坐标系的关系。将鼠标事件的屏幕坐标,转换为canvas坐标系中的坐标。22 计算旋转时每一次mousemove,在旋转前的canvas坐标系中move的角度。23

24

25 varcvs=document.getElementById("canvas");26 varctx=cvs.getContext("2d");27 varcvsH=cvs.height;28 varcvsW=cvs.width;29 varbeginX,beginY;30 varLT={x:30,y:30};//图片左上角的点

31 varSelected_Round_R=12;32 varisDown=false;33 varimgH,imgW;34 varmoveAble=false,rotateAble=false;35 varimg= newImage();36 varrotate_radian=0;//canvas坐标系x轴与屏幕坐标系X轴夹角弧度

37 img.src="img/niuniu.jpg";38 img.οnlοad=function(){39 imgH=img.height;40 imgW=img.width;41 PO={x:LT.x+imgW/2,y:LT.y+imgH/2};42 ctx.translate(PO.x,PO.y);//载入时将canvas坐标系原点移到图片中心点上

43 onDraw();44

45 }46 functiononDraw(){47 ctx.clearRect(-cvsW,-cvsH,2*cvsW,2*cvsH);48 ctx.drawImage(img,-imgW/2,-imgH/2);49 //旋转控制旋钮

50 ctx.beginPath();51 ctx.arc(0,-imgH/2-Selected_Round_R,Selected_Round_R,0,Math.PI*2,false);

52 ctx.closePath();53 ctx.lineWidth=2;54 ctx.strokeStyle="#0000ff";55 ctx.stroke();56 }57 cvs.addEventListener("mousedown", startMove,false);58 cvs.addEventListener("mousemove", moving,false);59 cvs.addEventListener("mouseup", endMove,false);60 cvs.addEventListener("mouseout",endMove,false);61

62 functionimgIsDown(x,y){63 return(-imgW/2<=x && x<=imgW/2 && -imgH/2

67 varbool=getPointDistance({x:x,y:y},round_center)<=Selected_Round_R;68 returnbool;69 }70 functionstartMove(){71 event.preventDefault();72 isDown=true;73 varloc=getEvtLoc();//获取鼠标事件在屏幕坐标系的位置(原点在canvas标签左上角)

74 varx=loc.x,y=loc.y;75 beginX=x,beginY=y;76 varcLoc=convertCoor(loc);77 varXc=cLoc.x,Yc=cLoc.y;78 moveAble=imgIsDown(Xc,Yc);79 rotateAble=RTIsDown(Xc,Yc);80 if(moveAble) cvs.style.cursor="move";81 if(rotateAble) cvs.style.cursor="crosshair";82 }83 functionmoving(){84 event.preventDefault();85 if(isDown==false)return;86 varloc=getEvtLoc();87 if(moveAble){88 varx=loc.x,y=loc.y;89 vardx=x-beginX,dy=y-beginY;90 varmPO={x:PO.x+dx,y:PO.y+dy};//因为鼠标移动dx dy,所以PO在屏幕坐标系的坐标也 移动dx dy

91 varcPO=convertCoor(mPO);//屏幕坐标系移动后的PO转换成canvas坐标系的坐标

92 ctx.translate(cPO.x,cPO.y);//canvas坐标系原点移动到新的图片中心点

93 onDraw();94

95 PO.x=PO.x+dx;//记录下屏幕坐标系上PO的坐标变化

96 PO.y=PO.y+dy;97 beginX=x,beginY=y;//记录移动后鼠标在屏幕坐标系的新位置

98 }else if(rotateAble){99 varcLoc=convertCoor(loc);100 varXc=cLoc.x,Yc=cLoc.y;101 varnewR=Math.atan2(Xc,-Yc);//在旋转前的canvas坐标系中 move的角度(因为旋钮在上方,所以跟,应该计算 在旋转前canvas坐标系中,鼠标位置和原点连线 与 y轴反方向的夹角)

102 ctx.rotate(newR);103 rotate_radian+=newR;104 onDraw();105 }106 }107 functionendMove(){108 event.preventDefault();109 isDown=false;110 moveAble=rotateAble=false;111 cvs.style.cursor="auto";112 }113

114 functiongetEvtLoc(){//获取相对canvas标签左上角的鼠标事件坐标

115 return{x:event.offsetX,y:event.offsetY}116 }117

118 functionconvertCoor(P) {//坐标变换 屏幕坐标系的点 转换为canvas坐标系的点

119 varx=P.x-PO.x;//在屏幕坐标系中,P点相对canvas坐标系原点PO的偏移

120 vary=P.y-PO.y;121

122 if(rotate_radian!=0){123 varlen=Math.sqrt(x*x+y*y);124 varoldR=Math.atan2(y,x);//屏幕坐标系中 PO与P点连线 与屏幕坐标系X轴的夹角弧度

125 varnewR=oldR-rotate_radian;//canvas坐标系中PO与P点连线 与canvas坐标系x轴的夹角弧度

126 x=len*Math.cos(newR);127 y=len*Math.sin(newR);128 }129

130 return{x:x,y:y};131 }132 //获取两点距离

133 functiongetPointDistance(a,b){134 varx1=a.x,y1=a.y,x2=b.x,y2=b.y;135 vardd=Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));136 returndd;137 }138

139

140

图片拖拽+旋转 坐标转换demo

三、总结

在canvas上绘制的元素比较多的时候,不适合用这种办法进行拖拽旋转,因为时刻变换的坐标系会影响到canvas上的其他元素,增加其他元素绘制的复杂性。

有时间再研究save和restore在以上需求中的应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值