服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

图形编辑器:标尺功能的实现

日期: 来源:前端西瓜哥收集编辑:西瓜

大家好,我是前端西瓜哥。今天我们来实现图形编辑器的标尺功能。

项目地址:

https://github.com/F-star/suika

线上体验:

https://blog.fstars.wang/app/suika/

标尺指的是画布上边和左边的两个有刻度的尺子,作用让用户知道他正在编辑的视口所在位置范围。

我们的需求是:间隔特定的长度,绘制一个刻度,并显示这个刻度在 X 轴或 Y 轴上的位置。

先看最终实现效果:

标尺功能演示

可以看到,视口移动后,标尺上的刻度能正确地改变。此外缩放画布,标尺的步长会发生改变,保持一个比较适合的密度。

实现思路

总体实现思路:

  1. 确定刻度尺的步长(step)。步长是和画布缩放比(zoom)相关的,zoom 越大,step 就越小;
  2. 计算出需要绘制的所有刻度。分别为从视口从左侧到右侧,从上边到下边的范围;
  3. 绘制。绘制上也是有考量的,先绘制背景,然后绘制刻度,最后绘制分界线。

步长选择

步长会根据 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);

绘制顺序

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

  1. 绘制两个标尺的背景色;
  2. 绘制刻度值;
  3. 用一个和背景色同色的矩形盖掉左上角那个方形,那个地方不能有刻度值,不如两个标尺的刻度会重叠。你也可以在绘制刻度值时,用裁切(ctx.clip)不让绘制到那个方形区域上;
  4. 绘制两条分割线;

最后

标尺实现大致如此,并不复杂。

我是前端西瓜哥,欢迎关注我,学习更多前端知识。



相关阅读,

图形编辑器:旋转选中的元素

图形编辑器:场景坐标、视口坐标以及它们之间的转换

图形编辑器——矩形选区是如何实现选中多个图形的?

在容器内显示图片的五种方案:contain、cover、fill、none、scale-down

计算机图形学:变换矩阵

快速检索碰撞图形:四叉树碰撞检测

Canvas 性能优化:脏矩形渲染

如何在 Canvas 上实现图形拾取?

来,教你开发一款图形编辑器


关注公众号,后台回复 「字节」,即可获得字节前端面试资料

相关阅读

  • 图形编辑器:图形和辅助线绘制的坐标问题

  • 大家好,我是前端西瓜哥。今天看看绘制图形和辅助线时,坐标转换的一些注意点。项目地址,欢迎 star:https://github.com/F-star/suika线上体验:https://blog.fstars.wang/app/suika
  • 回顾 2022 / 展望 2023

  • 其实不想写的,因为今年挺糟心的,下不动笔。但快过年了,思前想后,最后还是写一下吧。回顾 2022先过一下 2022 立下的 flag。1、每个月至少三篇文章,再尝试看能不能投稿。这个对我
  • 图形编辑器:工具管理和切换

  • 大家好,我是前端西瓜哥。今天我们看看对于一款图形编辑器,应该怎么去实现工具,比如绘制矩形、选中工具,以及如何去管理它们的。项目地址,欢迎 star:https://github.com/F-star/sui
  • WebSocket 入门:简易聊天室

  • 大家好,我是前端西瓜哥,今天我们用 WebSocket 来实现一个简单的聊天室。WebSocket 是一个应用层协议,有点类似 HTTP。但和 HTTP 不一样的是,它支持真正的全双工,即不仅客户端可以
  • 在 VSCode 中像写 TypeScript 一样写 JavaScript

  • 大家好,我是前端西瓜哥。我们在 VSCode 编辑器中编写 js 代码,是会提供类型提示的。VSCode 会推断一个变量是什么类型,并在你输入内容的时候,提供对应的 API 属性或方法补全。如
  • 一起学 pixijs(2):修改图形属性

  • 大家好,我是前端西瓜哥。我们做动画、游戏、编辑器,需要根据用户的交互等操作,去实时地改变图形的属性,比如位置,颜色等信息。今天西瓜哥带大家来看看在 pixijs 怎么修改图形的属
  • Amazing!如何根据背景色自动切换黑白文字?

  • 在项目中,经常会碰到背景色不确定的场景,为了让内容文字足够清晰可见,文字和背景之间需要有足够的对比度。换句话说,当背景是深色时,文字为白色,当背景是浅色时,文字为黑色,就像这样
  • 巧用视觉障眼法,还原 3D 文字特效

  • 最近群里有这样一个有意思的问题,大家在讨论,使用 CSS 3D 能否实现如下所示的效果:这里的核心难点在于,如何利用 CSS 实现一个立体的数字?CSS 能做到吗?不是特别好实现,但是,如果仅
  • 那些炫酷的 CSS 文字效果之诗词《兔》

  • 不知不觉已经迈入2023年,今年是兔年,想到兔子就会联想到玉兔,中秋,胡萝卜,兔子不吃窝边草,这就越扯越远了,今天的主题是用纯CSS来实现各种不错的文字效果,文字则摘录古诗词中有关《
  • 今晚 B 站直播预告 - 《你所不知道的CSS》

  • 扫码直达 B 站直播间,7 号晚 20:00 点准时开始,预计时长 1 小时,感兴趣来听听:Hi 各位小伙伴,本周六(2023/01/07)晚 20:00 点,我将在 Bilibili 给大家带来一场《你所不知道的CSS》

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • 图形编辑器:标尺功能的实现

  • 大家好,我是前端西瓜哥。今天我们来实现图形编辑器的标尺功能。项目地址:https://github.com/F-star/suika线上体验:https://blog.fstars.wang/app/suika/标尺指的是画布上边和
  • 图形编辑器:图形和辅助线绘制的坐标问题

  • 大家好,我是前端西瓜哥。今天看看绘制图形和辅助线时,坐标转换的一些注意点。项目地址,欢迎 star:https://github.com/F-star/suika线上体验:https://blog.fstars.wang/app/suika
  • 回顾 2022 / 展望 2023

  • 其实不想写的,因为今年挺糟心的,下不动笔。但快过年了,思前想后,最后还是写一下吧。回顾 2022先过一下 2022 立下的 flag。1、每个月至少三篇文章,再尝试看能不能投稿。这个对我
  • 图形编辑器:工具管理和切换

  • 大家好,我是前端西瓜哥。今天我们看看对于一款图形编辑器,应该怎么去实现工具,比如绘制矩形、选中工具,以及如何去管理它们的。项目地址,欢迎 star:https://github.com/F-star/sui
  • WebSocket 入门:简易聊天室

  • 大家好,我是前端西瓜哥,今天我们用 WebSocket 来实现一个简单的聊天室。WebSocket 是一个应用层协议,有点类似 HTTP。但和 HTTP 不一样的是,它支持真正的全双工,即不仅客户端可以
  • 在 VSCode 中像写 TypeScript 一样写 JavaScript

  • 大家好,我是前端西瓜哥。我们在 VSCode 编辑器中编写 js 代码,是会提供类型提示的。VSCode 会推断一个变量是什么类型,并在你输入内容的时候,提供对应的 API 属性或方法补全。如