图形编辑器开发:加新的图形类型,触发丝滑小连招

大家好,我是前端西瓜哥。

这篇文章是春节前的最后一篇文章,西瓜哥在这里提前祝大家新年快乐了。

在搭好图形编辑器的框架后,我们可能需要根据需求加入一些新的图形类型。

那么加入新的图形类型,需要做哪些工作呢?今天我们就来探究一下。

图形属性设计

首先是设计新图形的属性,因为我们需要把这些数据进行持久化。

比如圆形的 center、radius,多边形的 points、closed 等。

如果是做的竞品,可以参考竞品的数据结构,如果可以拿到的话。这对理解需求,以及减少后期破坏性修改是有很大帮助的。

不要保存冗余数据

数据结构要尽量简洁,尽量不要保存一些多余的数据。

举个例子,对于矩形,x,y,width 和 height 是必要属性,但它的中点 centerX 和 centerY 就没有必要保存,它是基于前面 4 个属性的计算而来的 计算属性(其实算是一种缓存了)

另外这样可能还会出现数据不一致问题,如果保存的 centerX 和 centerY 和 x、y、width、height 计算出来的值不一致,那就会让人困惑,到底以谁为准呢。(更新维护缓存永远是让人头疼的问题)

但有一种计算属性可以考虑保存的,那就是计算性能开销大的数据,比如图形三角化的数据,复杂图形的包围盒,如果能够把它们保存下来,可以有效减少图纸的初次加载时间。

另外有一些属性是不会持久化的,它们只在内存中使用,比如图形可能会有版本号 version,会在属性更新时变更,可以用来判断是否某个版本的缓存是否还有效,持久化的时候则没有保存的意义。

图形渲染实现

设计好图形数据结构后,接着就需要基于这些属性去渲染图形。

对于基本图形,比如矩形、线、多边形,会使用渲染引擎去完成。渲染引擎可以自己实现,也可以基于开源的图形引擎,比如 Pixijs、ZRender,这里不多说。

然后是复杂图形。复杂图形通常就是基础图形的组合。这里会涉及到 一些简单的几何算法

比如,下面是一个名为 "基准符号" 的简易版图形的渲染效果。

该图形使用的属性为:

  1. startPoint:起点;

  2. endPoint:终点;

  3. size:矩形的尺寸,以及等边三角形的边长;

  4. text:文字内容。

数据结构很精炼,该图形可以用一个三角形、一条线、一个矩形加一个文字组合而成。

一些简单图形的信息需要实现的通过算法得到,其中最重要的两个算法为:

  1. 两个点表示的等边三角形,求它的所有顶点;

  2. 给一条线段,求延长线经过大小为 size 的中心的邻接矩形及矩形中点。

这需要你掌握一些简单的几何算法知识的,涉及到向量、三角函数。

用简单图形进行组合,一般情况下看起来没什么毛病,但在一些场景会 "露馅",比如设置透明度的时候会看到颜色更深的重叠区域

对于工业设计软件,这是用户可以接受的范围。

但如果是 UI 设计稿,那大概是不能接受了,这样也同样需要使用几何算法处理连接处过渡的。偷懒的话,可以找个布尔运算的库帮你处理(比如 Skia)。或者直接在渲染引擎里的三角化中计算。

绘制工具实现

图形设计好了,但用户怎么将图形绘制出来呢?

为此我们需要实现绘制工具,让用户通过鼠标和键盘,绘制图形的过程。

最简单的做法是,点一下,直接把图形放到画布中心上。或者拖拽到画布中,适合有大量图形类型的场景,这些图形通常用户也可以做一些简单的自定义。创建时不能定义属性值问题不大,只要之后能更改属性就行。典型代表有:drawio、Canva。

然后是交互好一些的,可以通过一些简单的鼠标行为完成图形的绘制。比如矩形,鼠标按下时确定矩形的左上角位置,鼠标释放确定第二个位置,构成一个矩形。典型代表有:Figma、Excaildraw。

然后就是极度复杂的交互,这个会在工业设计软件看到。

比如 AutoCAD 的一个绘制矩形工具,在绘制过程中可以通过输入命令,进入不同的子阶段,进而设置矩形的旋转角度、面积、宽高等值。

说真的,太复杂了,很多子阶段很少会用到,我不是很喜欢这种设计,感觉是为了复杂而复杂。

实现绘制工具的过程中,自然也离不开一些几何算法。比如吸附在某个图形的一条直线上,要实现正交效果,让绘制的点受到限制。

绘制图形可能有多个阶段,比如绘制多边线,用连续的多次鼠标按下释放绘制多个点,可能还要监听热键,将某段直线转换为绘制圆弧等等。

通过控制点更新属性

图形需要实现一个返回自定义控制点数组的方法。

可以使用配置化的方式,大概如下

javascript 复制代码
class GraphA {
  getHandlePoint() {
    return [
      {
        type: 'cornerRadiusLeftTop', // 左上角圆角控制点
        handleType: 'rect' // 控制点样式,使用矩形
        x: 9,
        y: 100,
        size: 5
        rotation:0.1342376,
      }
      // ...
    ]
  }
}

图形编辑器框架会在必要的时候,比如当前图形被选中的时候,调用该方法拿到信息生成控制点,渲染在画布顶层。

以及要实现用户拖拽这些自定义控制点的方法。

typescript 复制代码
updateAttrsByHandle(type,newPoint) {
 if (type === 'cornerRadiusLeftTop') {
    // ...
    this.cornerRadius = newVal
  }
  // ...
}

属性面板更新属性

除了可以用控制点修改图形属性,还要支持通过属性面板显示和修改属性。

属性面板的属性值也可以用配置的方法实现。

javascript 复制代码
getInfoPanelAttrs() {
  return [
    {
      getVal: () => this.x,
      uiType: 'number', // 输入框组件类型
      precision: 2, // 组件配置项
    },
    {
      key: () => this.text,
      uiType: 'string',
      maxLen: 18,
    },
    // ...
  ]
}

图形属性并不一定和 UI 上的属性一致。

例如我们用 rotation 属性保存旋转角,存的是弧度,对应的输入框考虑到用户体验,显示的值是角度值。

对此,我们需要实现两种数据格式的互转的方法。

其他业务逻辑

此外就是新的图形类需要重写的各种其他逻辑。

这个就看图形编辑器支持的高级功能的多少了,比如

  1. 复制粘贴处理,需要处理关联的其他对象。比如新的图形类型使用了一个全局的 style 对象,复制的时候就要把这个 style 也一起复制。

  2. 计算 bbox 包围盒方法

  3. 文件格式转换实现,比如 toSVG

  4. ...

兼容旧版编辑器

如果是单机软件,会有版本兼容问题

比如 2024 版新增的图形类型,在 2023 版是无法识别的,代码里就没有对应的处理逻辑。

糟糕的做法是旧版软件加载新版图纸时,弹个弹窗,说 "不好意思,版本过低,请购买我们的最新版软件"。

稍微好一点点的是,可以打开图纸,然后把无法识别的图形类型都忽略掉,不渲染,但依旧半斤八两。

好的做法是 想办法显示出来,做法是让新的图形类型,额外保存一个基础图形组合。

css 复制代码
{
  type: 'newGraph2024'
  extract: [
    { type: 'triangle', /**/ },
    { type: 'line', /**/ },
    { type: 'rect', /**/ }
  ]
}

这样旧版编辑器虽然没有新图形对应逻辑,但可以从 extract 数组中,拿到等价的图形进行渲染。当然新的图形类型特有的更新操作还是无法做到。

另外这个顺便还能实现图形的打散功能:一个图形分解为多个基础图形。

如果是联网才能用,不提供单机版,那就没有兼容问题

因为用户每次打开网页,都是最新版的编辑器。此外,因为数据是保存在服务端的,甚至可以对已有图形类型进行破坏性修改,修复一些前期不合理的属性设计。

典型的例子是 Figma,它没有单机版本,即使桌面端也需要联网才能使用。

Figma 不愿意主动公开设计文件的格式,这样做其实就等价于公布了一个特定版本的单机软件,是需要对自己公开的格式负责的,这对 Figma 的后续文件格式调整是不利的。

当你需要为客户提供单机模式的软件,你可就要小心谨慎地设计数据结构了,你没有太多后悔药可吃的。

结尾

总结一下,加一个图形类型,需要做的工作有:

  1. 图形属性设计

  2. 图形渲染实现

  3. 绘制工具实现

  4. 控制点更新属性

  5. 属性面板更新属性

  6. 其他业务逻辑

  7. 兼容旧版编辑器

这里有很多逻辑并不需要你从零到一实现,是可以通过继承父类的方式复用的,你只需要重写部分的方法即可。

另外你需要解决大量的几何问题,通常都不难,但数量多,常用的几何算法可以统一放到一个包里,方便复用。

其他的业务逻辑通常框架帮我们做好了,倒没啥问题。不够有时候需求超出了框架本身的能力,这时候就要改改框架了。

我是前端西瓜哥,欢迎关注我,学习更多图形编辑器知识。


相关阅读,

完美解析!Figma CTO 写的 fig 文件格式解析工具

图形编辑器开发:实现缩放图形

图形编辑器开发:快捷键的管理

图形编辑器开发:实现图形的复制粘贴

图形编辑器开发:最基础但却复杂的选择工具

图形编辑器:历史记录设计

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

图形编辑器:底层设计

相关推荐
摇摇奶昔x18 分钟前
webpack 学习
前端·学习·webpack
阿珊和她的猫36 分钟前
Vue Router中的路由嵌套:主子路由
前端·javascript·vue.js
_龙小鱼_1 小时前
Kotlin 作用域函数(let、run、with、apply、also)对比
java·前端·kotlin
霸王蟹1 小时前
React 19中如何向Vue那样自定义状态和方法暴露给父组件。
前端·javascript·学习·react.js·typescript
小野猫子1 小时前
Web GIS可视化地图框架Leaflet、OpenLayers、Mapbox、Cesium、ArcGis for JavaScript
前端·webgl·可视化3d地图
shenyan~1 小时前
关于 js:9. Node.js 后端相关
前端·javascript·node.js
uwvwko1 小时前
ctfshow——web入门254~258
android·前端·web·ctf·反序列化
所待.3832 小时前
深入解析SpringMVC:从入门到精通
前端·spring·mvc
逃逸线LOF2 小时前
CSS之精灵图(雪碧图)Sprites、字体图标
前端·css
海天胜景3 小时前
jqGrid冻结列错行问题,将冻结表格(悬浮表格)与 正常表格进行高度同步
前端