大家好,我是前端西瓜哥。今天我们来实现图形编辑器的标尺功能。
成都创新互联主营杏花岭网站建设的网络公司,主营网站建设方案,重庆App定制开发,杏花岭h5微信小程序开发搭建,杏花岭网站营销推广欢迎杏花岭等地区企业咨询
项目地址:
https://github.com/F-star/suika
线上体验:
https://blog.fstars.wang/app/suika/
标尺指的是画布上边和左边的两个有刻度的尺子,作用让用户知道他正在编辑的视口所在位置范围。
我们的需求是:间隔特定的长度,绘制一个刻度,并显示这个刻度在 X 轴或 Y 轴上的位置。
先看最终实现效果:
标尺功能演示
可以看到,视口移动后,标尺上的刻度能正确地改变。此外缩放画布,标尺的步长会发生改变,保持一个比较适合的密度。
总体实现思路:
步长会根据 zoom 进行设置,目的是让视口中的标尺能绘制适宜密度的刻度。
假设我们的步长固定为 50,不跟随 zoom 改变,在 100% 看起来效果不错:
但当你缩小时,会变成下面这样:
密度过大,导致数字重叠。同样,放大时则过于稀疏,刻度很难才见到一个,没能发挥标尺的效用。
步长怎么计算呢?
理论上步长可以是 50,那么 51 好像也行,3 也行。但更建议使用 5 的倍数、2 的倍数、25 的倍数这些作为步长。
因为没有什么理论参考,所以我还是选择参考市面上的设计工具的步长变化设计。
比如 figma,zoom 落在 [100%, 200%) 的步长为 50,[200%, 500%) 则是 10 等等。
我的实现为:
const getStepByZoom = (zoom: number) => {
// 可用的步长列表
const steps = [1, 2, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000];
// 看着 figma 的 step 变化想出的一个奇怪的规律
// 然后找出可选步长列表最近的并大于它的 step 作为最终步长
const step = 50 / zoom;
for (let i = 0, len = steps.length; i < len; i++) {
if (steps[i] >= step) return steps[i];
}
return steps[0];
};
const step = getStepByZoom(zoom);
这里我讲解水平(x 轴)方向的情况。垂直方向同理,就不赘叙了。
首先计算出视口最左侧和最右侧的 x 坐标值。
let startXInScene = viewport.x + startXInViewport / zoom; // 视口坐标转场景
let endXInScene = viewport.width + startYInViewport / zoom; // 视口坐标转场景
然后找离它们最近的落在刻度上的值。
对此,我实现了一个 getClosestVal 方法。
/**
* 找出离 value 最近的 segment 的倍数值
*/
const getClosestVal = (value: number, segment: number) => {
const n = Math.floor(value / segment);
const left = segment * n;
const right = segment * (n + 1);
return value - left <= right - value ? left : right;
};
startXInScene = getClosestVal(startXInScene, step);
endXInScene = getClosestVal(endXInScene, step);
得到起点和终点,我们可以开始循环了,从 startXInScene 开始,每次循环加一个 step,直至达到末尾为止。
ctx.textAlign = 'center'; // 文字水平居中对齐
while (startXInScene <= endXInScene) {
ctx.strokeStyle = setting.rulerMarkStroke;
ctx.fillStyle = setting.rulerMarkStroke;
// 场景转回视口再绘制。刻度线不能直接在场景中绘制,因为缩放变换会导致线的粗细变化
const x = (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 度的旋转。
export const rotateInCanvas = (
ctx: CanvasRenderingContext2D,
angle: number,
cx: number,
cy: number
) => {
ctx.translate(cx, cy);
ctx.rotate(angle);
ctx.translate(-cx, -cy);
};
rotateInCanvas(ctx, -HALF_PI, x, y);
绘制顺序需要注意一下,先后顺序为:
标尺实现大致如此,并不复杂。
分享名称:图形编辑器:标尺功能的实现
分享URL:http://www.36103.cn/qtweb/news39/32839.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联