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

项目地址:

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. 绘制两条分割线;

最后

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

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

图形编辑器:标尺功能的实现相关推荐

  1. 图形编辑器:对齐功能的实现

    大家好,我是前端西瓜哥.这次来简单说说实现图形编辑器对齐功能的思路. 对齐功能 实现图形编辑器的对齐功能. 编辑器 github 地址: github.com/F-star/suik- 线上体验: b ...

  2. 图形编辑器:工具管理和切换

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

  3. canvas图形编辑器

      原文地址:http://jeffzhong.space/2017/11/02/drawboard/   使用canvas进行开发项目,我们离不开各种线段,曲线,图形,但每次都必须用代码一步一步去实 ...

  4. Grafana 仪表盘和图形编辑器

    Grafana 是一个跨平台.开源的数据可视化网络应用程序平台.用户配置连接的数据源之后,Grafana 可以在浏览器显示数据图表和警告.该软件的企业版本提供更多的扩展功能.扩展功能通过插件的形式提供 ...

  5. R语言图形编辑器GUI开发环境RStudio安装(Windows操作系统下、RStudio开发环境安装)

    R语言图形编辑器GUI开发环境RStudio安装(Windows操作系统下.RStudio开发环境安装) RStudio是R语言的集成开发环境(IDE),它是一个独立的开源项目,它将许多功能强大的编程 ...

  6. unity2d游戏开发系列教程:二、新建工程并熟悉Unity编辑器常用功能

    目录 unity2d游戏开发系列教程:一.环境安装 第一步.打开项目 耐心等待一小会 工程界面 第二步.创建第一个场景(第一关)进行试玩 点击图中标号1的运行按钮,即可简单试玩感受,操作如下 移动A, ...

  7. GPS数据矢量化JAVA_SVGX矢量化图形编辑器,100%JAVA实现的矢量化图形编辑器

    SVGX矢量化图形编辑器,100%JAVA实现的矢量化图形编辑器 SVGX矢量化图形编辑器是面向工程应用的矢量图形制作软件,基于著名的Eclipse GEF图形编辑框架实现了W3C SVG 1.1规范 ...

  8. vue+vuex实现2D可视化图形编辑器

    随着物联网的快速发展,人们对物联网设备的数据监控可视化的需求越来越强烈,为了解决企业设备数据监控可视化痛点,深圳当康科技经过不断的努力,研发了一套基于物联网的可视化图形编辑器,用户编辑器可以通过该编辑 ...

  9. 组态王图素制作_组态王的图形及动画功能

    组态王工具箱中的基本图素:直线.扇形.填充图形(封闭图形,内部可填充色彩,有:椭圆和圆角矩形).折线.管道.多边形.文本.按钮和点位图,它们均具有图形及动画功能.它们中填充图形类动画连接框如图1所表示 ...

最新文章

  1. mysql-cluster 安装配置
  2. php定义枚举,PHP中Enum(枚举)用法实例详解
  3. c语言 int top,顺序栈(C语言,静态栈)
  4. 利用matlab命令画出以下信号的波形,MATLAB实验报告
  5. Python里那些可爱的游戏模块们
  6. Kubernetes用户指南(二)--部署组合型的应用、连接应用到网络中
  7. Flutter - 生成二维码与识别二维码
  8. phpdesigner8 php7.0,大家千万别用PHPDesigner8 的项目替换,多说是泪,改整个站点中!
  9. websocket php MySQL_PHP写了一个websocket服务,mysql连接实例丢失问题怎么解决?
  10. div中赋值html字符串
  11. Emscripten 单词_分享15个英语单词记忆方法,简单实用,赶紧收藏吧!
  12. PHPExcel 插件使用详解
  13. DSD解码ES9038PRO和AK4497
  14. html代码word,Web前端
  15. 2020/03/01 03-Django模板DTL使用
  16. iOS性能优化之耗电量
  17. DOM基础详细 包含多个案例
  18. 使用数组实现购物车的应用
  19. Web前端--HTML+CSS+JS新型冠状病毒射击小游戏
  20. 插件 实用的窗口置顶小工具 TopMost

热门文章

  1. LWRP光照贴图异常
  2. 算法学习-动态规划,纸老虎打倒他(持续更新中)
  3. 华为鸿蒙或适配高通平台,鸿蒙2.0适配高通平台,网友:华为这是要取代安卓吗?...
  4. 分享ActionScript视频系列教程——第31讲 聊天室程序
  5. vue.js中watch的六种用法(附实例解析)
  6. 【九天·毕昇】一个免费获得GPU的算力平台
  7. 基于双目视觉的自动驾驶技术
  8. Java_Day 接口、多态
  9. springboot结合hessian的使用
  10. 不是秘密的内幕 常见移动电源质量问题