大家好,我是前端西瓜哥。今天我们来实现图形编辑器的标尺功能。项目地址:https://github.com/F-star/suika在线体验:https://blog.fstars.wang/app/suika/尺子指的是画布左上角的两个刻度尺,作用让用户知道他正在编辑的视口的位置范围。我们的需求是:间隔一个特定的长度,画一个刻度,显示刻度在X轴或Y轴上的位置。先看最终效果:尺子功能演示,移动视口后,可以正确改变尺子上的刻度。此外,当画布缩放时,标尺的步长会发生变化,以保持更合适的密度。实现思路总体实现思路:确定尺度的步长(step)。步长与画布缩放比例(zoom)有关。zoom越大,step越小;计算所有需要绘制的比例。分别从视口从左到右,从上到下的范围;绘画。画的时候也有注意事项,先画背景,再画比例尺,最后画分界线。步长选择步长将根据缩放设置,以便视口中的标尺可以绘制适当密度的刻度。假设我们的步长固定为50,不随缩放变化,100%看起来还不错:但是当你缩小时,就会变成这样:密度太大,导致数字重叠。同样,放大的时候太稀疏了,很难看到一个刻度,没有起到刻度的作用。如何计算步长?理论上步长可以是50,那么51好像也行,3也行。但更推荐使用5的倍数、2的倍数、25的倍数作为步长。因为没有理论参考,所以还是选择参考市面上设计工具的阶跃变化设计。比如figma中zoom落在[100%,200%)的步长为50,[200%,500%)为10等。我的实现是:constgetStepByZoom=(zoom:number)=>{//可用步骤列表conststeps=[1,2,5,10,25,50,100,250,500,1000,2500,5000];//看figma中的step变化,想出了一个奇怪的规则//然后找到距离optionalstep列表最近且大于它的step作为最后一步conststep=50/zoom;for(leti=0,len=steps.length;i=step)返回步骤[i];}返回步骤[0];};conststep=getStepByZoom(缩放);计算范围这里我说明一下水平(x轴)方向的情况。垂直方向也是如此,这里不再赘述。首先计算视口最左边和最右边的x坐标值。让startXInScene=viewport.x+startXInViewport/zoom;//到场景的视口坐标letendXInScene=viewport.width+startYInViewport/zoom;//视口坐标到场景并在比例尺上找到最接近的值。为此,我实现了一个getClosestVal方法。/***找到最接近值的段的倍数*/constgetClosestVal=(value:number,segment:number)=>{constn=Math.floor(value/segment);constleft=段*n;constright=段*(n+1);返回值-左<=右-值?左右;};startXInScene=getClosestVal(startXInScene,步骤);endXInScene=getClosestVal(endXInScene,步骤);我们可以开始循环,从startXInScene开始,每次都加一个step,直到结束。ctx.textAlign='居中';//将文本水平居中对齐while(startXInScene<=endXInScene){ctx.strokeStyle=setting.rulerMarkStroke;ctx.fillStyle=setting.rulerMarkStroke;//将场景转回视口并再次绘制。场景中不能直接绘制比例线,因为缩放变换会导致线的粗细发生变化constx=(startXInScene-viewport.x)*zoom;//绘制比例尺ctx.beginPath();ctx.moveTo(x,y);ctx.lineTo(x,y+setting.rulerMarkSize);ctx.stroke();ctx.closePath();//比例值使用场景坐标值ctx.fillText(String(startXInScene),x,y-4);//+step,指针移动startXInScene+=step;}垂直刻度也一样,只是刻度值的文字需要旋转-90度。exportconstrotateInCanvas=(ctx:CanvasRenderingContext2D,angle:number,cx:number,cy:number)=>{ctx.translate(cx,cy);ctx.rotate(角度);ctx.translate(-cx,-cy);};rotateInCanvas(ctx,-HALF_PI,x,y);绘制顺序需要注意,顺序是:绘制两个刻度的背景色;绘制刻度值;左上角用与背景色相同颜色的矩形遮盖住角上的正方形,那个地方不能有刻度值,因为两个刻度的刻度会重叠。也可以使用裁剪(ctx.clip)来防止在绘制比例值时绘制在正方形区域;画两条分界线;scale最终的实现大概是这样的,并不复杂。