背景:需要使用Canvas添加图片、文字、相机图片,支持拖拽、缩放、旋转等功能。但是成熟的Canvas库(比如Sprite.jsFabric.js)一般都比较大(300kb+),所以我们自己实现了一个简化版来减小体积。零基础功能:拖放移动元素缩放元素(变形、比例缩放)旋转元素元素内容可以是:图片、文字、相机画布中的元素(也叫Sprites),确定每个元素的位置信息通过IRect描述,交互可以体验Fabric.js。接口IRect{x:数字;y:数字;w:数字;h:number;}classSprite{//位置,大小数据rect:IRect//旋转角度angle:number}Canvas绘制元素,因为需要绘制元素(图片,文字,视频)所占的位置可以用一个来表示矩形,这样就可以://将画布坐标系的原点移动到矩形的中心点ctx.setTransform(1,0,0,1,x+w/2,y+h/2)//任意一个rect的绘制参数都是一样的ctx.drwaImage(img,-w/2,-h/2,w,h)//围绕中心点旋转ctx.rotate(angle)这一步很简单,就像拖动一样dom元素。//1.监听mousemove事件,鼠标位置减去初始位置,得到移动的坐标差。//startXstartY为鼠标按下时的位置incX=offsetX-startXincY=offsetY-startY//2.将移动距离映射到Canvas坐标系中的距离(因为CanvasclientWith很可能与width不同)。incX=incX/(canvas.clientWith/canvas.width)//incY相同//3.将新坐标设置为元素的位置。sprite.setRect({x:x+incX,y:y+incY})翻转元素一般通过右键菜单来触发翻转效果。收到事件后,选择以下方法之一。这两个方法都可以通过ctx.setTransform来实现。(建议,据说性能更好,不需要ctx.savectx.restore)//https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/setTransform//Horizo??ntalflipctx.setTransform(-1,0,0,1,0,0)//垂直翻转ctx.setTransform(1,0,0,-1,0,0)可以通过ctx.scale来实现。//https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/scale//水平翻转ctx.scale(-1,1)//垂直翻转ctx.scale(1,-1)旋转元素得到旋转的中心点,通过rect计算得到。{x:x+w/2,y:y+h/2}监听mousemove事件,获取鼠标坐标,根据鼠标坐标获取旋转角度angle=Math.atan2(y,x)。functionrotateSprite(centerPos,onChange){constonMove=({clientX,clientY}:MouseEvent):void=>{//映射到相对于中心点的坐标constx=clientX-centerPos.xconsty=clientY-centerPos.y//旋转控制点在正上方,相对于x轴为-90°,所以Math.PI/2constangle=Math.atan2(y,x)+Math.PI/2onChange(angle)}constclear=():void=>{window.removeEventListener('mousemove',onMove)window.removeEventListener('mouseup',clear)}window.addEventListener('mousemove',onMove)window.addEventListener('mouseup',clear)}缩放元素的rect缩放本身很简单,但是旋转之后,缩放效果就复杂多了。例如:拖动矩形右侧的控制点只能改变矩形的宽度。在旋转的情况下,矩形的左侧保持不变。拖动矩形右下角的控制点,同时改变宽高,但需要位置比例,矩形左上角的点保持不变。所以这道题可以抽象为给定的参数:rect(坐标,宽高)数据,旋转角度,鼠标坐标。待解答案:newIRect(坐标、宽高)。解题步骤以拖动右下角的控制点(RB)为例。因为它是按比例缩放的,所以中心点将始终在对角线上(LT-RB)。监听mousemove事件,获取鼠标坐标,减去圆心点坐标,得到相对于圆心点的坐标。根据坐标O、单元旋转角度和IRect,可以计算出RB被移动的长度。移动长度乘以对应的三角函数,就是增加的宽度和高度。//对角线角度(LB-RT对角线,角度为负,需要乘以-1)constdiagonalAngle=Math.atan2(rect.h,rect.w)//坐标系旋转角度,lb->rt的对角线的初始角度为负,所以需要乘以-1constrotateAngle=diagonalAngle+angle//startPos和RB(拖动起始位置)的坐标,ClientXY为mousemove事件中获取的坐标constox=clientX-startPos.xconstoy=clientY-startPos.y//坐标系旋转变化公式,让x轴与[对角线]重合,鼠标位置的x值就是增加的长度(RB'位于垂直鼠标与对角线的交点)constincS=ox*Math.cos(rotateAngle)+oy*Math.sin(rotateAngle)//按比例缩放,增加宽高等于长度乘以对应的角度函数//因为比例缩放,中心和被拖动必须在对角线上constincW=incS*Math.cos(diagonalAngle)constincH=incS*Math.sin(diagonalAngle)//新中心点坐标,原中心坐标(x+w/2,y+h/2)constnewCntX=incS/2*Math.cos(rotateAngle)+x+w/2constnewCntY=incS/2*Math.sin(rotateAngle)+y+h/2//矩形新坐标constnewX=newCntX-newW/2constnewY=newCntY-newH/2//newrect:{x:newX,y:newY,w:newW,h:newH}上述代码使用了坐标系旋转变化公式,其推导如下图所示:其他缩放场景注意事项的缩放左边的三个点,坐标值变小(负数),也就是放大,所以计算出来的值要乘以-1。善用坐标系旋转变化公式,拖动一些点时,注意其角度与默认X轴的不同。判断鼠标是否点击了元素将鼠标坐标和rectxy值转换成相对于中心点的坐标然后求鼠标坐标旋转rect角度后的坐标(坐标系旋转公式)判断是否鼠标点击的坐标超出了rect的边界//pos鼠标坐标,rectcentercenterpoint//鼠标点击坐标映射到canvas坐标,然后转换为以中点为原点的坐标constcvsOX=pos.x/ratio.w-center.xconstcvsOY=pos.y/ratio.h-center.y//如果有旋转,则映射到相对原点,旋转前的坐标letmx=cvsOXletmy=cvsOYlet{x,y,w,h}=rect//映射到中心点的坐标x=x-center.xy=y-center.y//坐标系旋转变化公式mx=cvsOX*Math.cos(angle)+cvsOY*Math.sin(angle)my=cvsOY*Math.cos(angle)-cvsOX*Math.sin(angle)if(mx
