我是前端西瓜哥,今天教你怎么开发一款图形编辑器。
虽然说的是图形编辑器,其实也归为编辑器,和文本编辑器开发的思想很多地方是共通的。如果你做文本编辑器的开发,也可以看看。
编辑器是比较复杂的项目,由多个功能模块组合而成。要想编辑器的代码有优秀拓展性、可维护性,分模块解耦是非常重要的事情。
编辑器开发的最大工作是在交互上,基本上是围绕鼠标左键事件进行交互,并搭配键盘快捷键。
计算图形学也有涉及,但基本都是简单的知识,比如判断两个矩形是否相交。高级的计算机图形学知识涉及较少,主要是高级功能才会用到,通常我会找第三方库来实现。
所以,不需要太多知识,我们也是可以开发一款图形编辑器的,就是比较花时间。
我们先说说编辑器的几个重要的模块。
坐标模块
说到图形界面,不得不提的就是坐标。
画布是可以缩放拖拽的,真实屏幕上的坐标并不就是编辑器画布上的坐标,所以需要做一层转换。
一个编辑器也可能有多个坐标系统。
画布模块
画布的拖拽移动,以及缩放。
拖拽的实现,可以修改画布左上角坐标,或者用 transform 的 translate。
至于缩放,需要考虑的东西挺多的:
- 通过鼠标缩放时,要考虑光标所在的位置,以此为缩放中心进行缩放。
- 考虑最小能缩小到多小,最大能放大到多少。
- 缩小到画布小于容器大小时,是否将画布居中。
- 缩放的过程是要设置固定的比例,还是让用户随意缩放出有小数的比例。
- 一键缩放比例 100% 或自适应窗口的逻辑。
历史记录模块
一款编辑器如果没有撤销重做功能,无论功能多么强大,它就是个半成品。
历史记录的实现通常为设计模式的 命令模式,将各种操作抽象成一个个命令类,在操作过程中创建的命令实例会维护到 undo 栈和 redo 栈中。
在编写交互逻辑时,需要注意 一些有过程性的操作不要生成多个命令。
比如移动元素在鼠标释放前,不要生成多个移动命令放入历史记录中,否则撤销不友好。
又比如文本编辑器,快速输入内容时,不要每输入一个字就生成一个操作命令。而是设置一个时间,多长时间没输入才生成命令放到历史记录中。
工具模块
编辑器的独特功能就体现在工具模块上了,其他模块属于是基础建设。
工具模块有什么?最基础的有:
- 选中移动工具。选中工具其实也很复杂:直接点击选中、选区选中、shift 多选少选、选中已经被选中的元素(移动)
- 绘制图形工具。矩形、圆形、贝塞尔曲线等。
- 拖拽画布工具。因为是高频操作。通常一些编辑器额外支持按着右键拖拽,或是按着空格键和鼠标左键拖拽。
工具类很多,那么怎么管理呢?
我们需要一个 toolManager 类,它的实现其实和 VueRouter
和 ReactRouter
一样的套路,只是我们不是通过修改路由来切换,而是点击工具按钮调用 API 来切换工具。切换时,对应的注册的工具模块会被挂载。
一个个工具模块,就好比 React 的组件,需要支持组建挂载、销毁等钩子。
在挂载的时候需要注册一些临时的快捷键事件方法,销毁时做事件方法销毁工作。
当然我们这个是交互比较重的编辑器,所以还要实现 mousedown、mouseover 之类的接口。
工具类要注重拓展性,最好做成插件的形式,让其他人可以自定义地添加自己开发的工具类。
图层模块
当一个事物变得复杂后,对其进行管理就非常重要,分组就是一种解决方案。
所以很多编辑器都支持图层。当然如果编辑器足够简单,可以不支持。
图层怎么理解?好比是一张张透明的纸叠在一起,然后我们从上往下看。然后我们分别在不同的纸上画画,顶部的非透明内容会遮挡住下方相同区域的内容。我们还可以隐藏或是移动其中任一图层,调整出我们想要的 “一幅画”。
如果没有图层,我们所有的内容都在一张纸上,我们就失去了很多可能性,基本上修改不能微调,要重画。
图层模块需要支持基础功能:
- 显示隐藏
- 锁定(无法操作。当然还有进阶的无法移动、透明像素锁定,可以考虑实现)
- 选中(就是进入图层)
- 重命名
- 删除
还有些高阶也算比较高频的功能:向下合并、创建图层蒙版。
其他模块
- 快捷键。分为全局快捷键和工具的局部快捷键。我看
hotkey-js
这个快捷键库貌似挺不错的。 - 标尺模块。画布的顶部和左侧的固定标尺,能够对齐。
- 元素相对位置辅助线展示
- ...
技术选型
因为模块非常多,也会为了支持新的功能做一些较大的改动,甚至会做较大的重构。所以弱类型的 JavaScript 还是算了,必须得 TypeScript。
考虑到可移植性,建议将编辑器分为底层能力的 core 和提供交互能力的 UI 层。
这样我们就可以较轻松迁移到 Vue、React 等框架上。就比如 VSCode 的核心编辑器被抽离出为 Monaco Editor,你在 LeetCode 刷题时默认编辑器就是这个 Monaco Editor。
所以你或许可以考虑 monorepo
的多项目开发策略?不过我没有试过。
图形编辑器在底层技术实现上主要有:
- Dom 元素:svg 或 div。比如 drawio、Boxy SVG、百度脑图。
- Canvas:比如各种在线协作表格、figma
考虑到通过元素过多导致的网页渲染卡顿,Canvas 可以做渲染上的优化,可以支持大量数据的场景。你可以看各大厂商的在线表格,都是 Canvas 做的,因为只有这样才能支持上万行的表格。
Canvas 是图形编辑器的主流,但在开发上也会变得麻烦:不好调试、且可能需要自己写一套渲染引擎。
需要的知识
因为编辑器很复杂,所以架构要做好,否则无法维护。为此,你需要学习设计模式。
此外你需要学习一些简单的计算机图形学知识,比如图形的碰撞检测、颜色填充的算法等。当你在开发时遇到对应问题再去研究就好了。有些复杂的图形学知识,可能需要找找第三方图形库帮你解决咯。
Canvas 或 SVG 的 API 学习,看你用到什么底层技术了。
结尾
本文是我开发编辑器的一些粗浅的见解,希望能帮到你。如果你有编辑器相关的问题,可以给我留言,我会考虑将其作为下一篇文章的主题。
如果觉得有帮助,希望你能给我点个赞,这样我才能有东西更新编辑器相关的文章。