文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JS图形编辑器实现标尺功能示例详解

2023-01-16 12:02

关注

正文

项目地址:

github.com/F-star/suik…

线上体验:

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 方法。


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);

绘制顺序

绘制顺序需要注意一下,先后顺序为:

以上就是JS图形编辑器实现标尺功能示例详解的详细内容,更多关于JS图形编辑器实现标尺的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯