图形编辑器:绘制图形和辅助线的坐标今天我们就来看看绘制图形和辅助线时坐标变换的一些注意事项。项目地址,欢迎star:https://github.com/F-star/suika在线体验:https://blog.fstars.wang/app/suika/Viewporttoscene:functionviewportCoordsToSceneCoords(x,y,scrollX,scrollY,zoom){return{x:scrollX+x/zoom,y:scrollY+y/zoom}}场景到视口:functionsceneCoordsToViewportCoords(x,y,scrollX,scrollY,zoom){return{x:(x-scrollX)*zoom,y:(y-scrollY)*zoom};}图形的绘制场景非常大,但是可以绘制的范围其实就是视口的大小。因此,我们需要将使用场景坐标的图形位置转换为视口坐标,然后进行绘制。有一个很低效的方法,就是生成一个非常大的Canvas画布,把所有的图形都画在里面,然后用一个div容器来存放,然后给div设置overflow:scroll。然后只需调整div的scrollLeft和scrollTop。不推荐,效率很低。对于图形,我们的做法是在绘制图形之前先做矩阵变换,这样后面绘制的所有像素点都会自动转换,而不是手动一个一个地转换。有朋友看了前面的sceneCoordsToViewportCoords方法,有:viewportX=(sceneX-scrollX)*zoom;所以他们认为ctx对应的变换是这样写的:ctx.translate(-viewport.x,-viewport.y);ctx.scale(zoom,zoom);//绘制各种图形//...这样写写的是对的,但是细节不对。因为ctx.scale的缩放中心因为之前的ctx.tranlate已经从(0,0)变成了(-viewport.x,-viewport.y)。正确的写法是缩放时先调整缩放中心,缩放后再往回移,即:ctx.translate(-viewport.x,-viewport.y);ctx.translate(viewport.x,viewport.y);ctx。缩放(缩放,缩放);ctx.translate(-viewport.x,-viewport.y);然后你会发现第一行和第二行有偏移,所以可以简化得到:ctx.scale(zoom,zoom);ctx.translate(-viewport.x,-viewport.y);//绘制各种graphics//...绘制辅助线的作用是将后面绘制的所有图形无差别地缩放。也就是说,当缩放变大时,线宽(ctx.lineWidth)也会变大。除了图形,图形编辑器还需要绘制另外一个很重要的东西:辅助线。(我们对于辅助线的坐标也是使用场景坐标系。)对于辅助线,我们希望在改变缩放的时候,线宽能够保持在原来的1px,控制点的大小保持不变不变,如下图所示:缩放功能演示解决方法是我们自己计算辅助线上的点在视口坐标中的位置,不需要借助ctx.scale和ctx.translate。//重置变换矩阵ctx.setTransform(1,0,0,1,0,0);//计算视口坐标系中的坐标值const{x,y}=sceneCoordsToViewportCoords(rotationX,rotationY,viewport.x,viewport.y,zoom)先用ctx.setTransform重新设置变换矩阵,消除之前的ctx.scale带来的影响。然后使用前面写的sceneCoordsToViewportCoords方法进行转换,得到视口坐标系中的位置,然后绘制。其实就是坐标系的局部变换,比如坐标会变换,线宽不会变换。其实还有另外一种思路,就是用缩放来划分线宽,或者说用缩放来划分大小。最后场景坐标和视口坐标的转换贯穿整个编辑器项目。还是很重要的,要仔细消化。
