利用两个多月的业余时间,使用Konva.js实现了一个名为【Pictode】的图形编辑器
缘起
在掘金上看到众多技术大咖打造自己的图形编辑器项目。通过阅读大咖们的文章让我深受启发,我既敬佩这些大咖坚持创作和分享的精神,也自豪于自己也有编辑器项目的经验。这些因素汇聚,催生了我开发一个自己的图形编辑器的想法。
虽然我以前在工作中已经实现了一些编辑器项目,如基于Three.js的3D编辑器、基于bpmn.js的流程编辑器、基于X6的拓扑编辑器,还有基于Vue的低代码编辑器等,但受公司项目限制,这些编辑器无法达到我理想中的水平。
今年8月份,我利用业余实践开始了名为Pictode
的图形编辑器项目。到目前为止,Pictode
集成了众多实用功能,如工具切换、属性编辑、右键菜单、本地保存、导出图片、画布缩放、画布拖拽、历史记录、语言切换、主题切换等 。经过这段时间努力,我认为Pictode
已初步具备雏形,因此我决定向大家介绍它。
Pictode主要功能演示
图形绘制
选择/变换
层级移动
对齐/分布
组合/解除组合
导出图片
温馨提示🔔:看图固然好,实际体验方知妙处!吾友,诚挚邀请前往pictode一试身手,领略其中奥妙! 🎨✨
Pictode能为你带来什么
也许你已经在掘金上看到许多技术大咖分享如何实现画板或图片编辑器的方法。他们或许详细解释了编辑器的原理和代码实现,有些甚至直接使用原生Canvas构建编辑器。但你是否和我有相同的感受?
我们既敬佩技术大咖,又感到自己阅读后除了感叹:"🐮🍺!"外收获很少。原因在于我们无法将他们的代码应用到项目中。虽然阅读、点赞和收藏了这些文章,但仍感头脑空空,实际收获有限。
把图形编辑器嵌入自己的项目,有时真麻烦。比如,编辑器用React构建,你的项目用Vue;或者编辑器不够灵活。Pictode就是你的救星,像🚀一样,解决这些问题,让你事半功倍。
故吾决心,以Pictode
引领特异之经验。Pictode
独立于UI框架,展示不朽之图形编辑构建。其API举重若轻,依托威能之Konva.js库。故不论尔用React,或Vue,亦或他框架,以Pictode
,成本可微,缔造专有图形编辑器,无往不利。
DIY画板
现在,让我们一起亲身体验Pictode
,并快速创建一个图形编辑器。
放心食用:DIY画板源码
创建画板
使用Pictode
创建画板只需要几个简单的步骤:
- 安装
@pictode/core
依赖包。该核心包负责管理舞台、图层、工具、插件,以及处理鼠标事件分发等功能。
shell
npm install @pictode/core
- 实例化
App
对象。
ts
import { App } from "@pictode/core";
const app = new App();
- 提供一个DIV容器,使用
app.mount(..)
方法挂在画布。
html
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { App } from "@pictode/core";
const canvasRef = ref<HTMLDivElement>();
const app = new App();
onMounted(() => {
if (canvasRef.value) {
app.mount(canvasRef.value);
}
});
</script>
<template>
<div class="wrapper">
<div ref="canvasRef"></div>
</div>
</template>
当然,在这一步,画布仍然是空白的。接下来,我们将介绍如何添加绘图工具。
绘图工具
Pictode
提供了一个基本的绘图工具包,其中包括了一些基于Konva.js的常用图形绘制工具。使用这些工具非常简单,只需几个步骤:
- 安装
@pictode/tools
依赖包。
shell
npm install @pictode/tools
- 实例化一个矩形工具。
ts
import { RectTool } from "@pictode/tools";
new RectTool({
config: {
stroke: "black",
strokeWidth: 2,
fill: "gray",
cornerRadius: 0,
opacity: 1,
},
})
- 使用
app.setTool(..)
方法切换到矩形工具。
ts
app.setTool(
new RectTool({
config: {
stroke: "black",
strokeWidth: 2,
fill: "gray",
cornerRadius: 0,
opacity: 1,
},
}),
);
- 在画布上通过鼠标开始绘制矩形。
选中/变换
现在,让我们为图形编辑器添加选中、平移、旋转和缩放等功能。Pictode
提供了名为 @pictode/plugin-selector
的插件,它简化了这些操作。以下是集成插件的步骤:
- 安装
@pictode/plugin-selector
依赖包。
shell
npm install @pictode/plugin-selector
- 实例化
selectorPlugin
插件。
ts
import { SelectorPlugin } from "@pictode/plugin-selector";
const selectorPlugin = new SelectorPlugin();
- 使用
app.use(selectorPlugin)
方法加载插件。
ts
app.use(selectorPlugin);
- 通过
鼠标点击
或app.select(...)
和app.cancelSelect(...)
方法来实现选中和取消选中图形的操作。同时,按下Shift
键可以实现多选和取消选择的功能。
选择工具
插入 selectorPlugin
后,你可能会观察到一个不太符合预期的行为:在绘制矩形时,蓝色框可能会叠加在矩形上,并且在移动矩形时,鼠标可能会继续绘制矩形。
这是因为 selectorPlugin
默认开启了框选功能,所以这时的 pictode
无法判断是要进行绘制操作还是框选操作。
解决方法很简单 :使用工具时禁用 selectorPlugin
,取消工具选择时重新启用。通过 app.setTool(tool)
启用工具时,禁用 selectorPlugin
;通过 app.setTool(null)
取消工具选择时,重新启用 selectorPlugin
,提升用户体验。
我明白,这种方式可能不是每个人都喜欢的。因此,@pictode/tools
提供了专门为 Selector
预留的工具 - SelectTool
,包括 onActive
和 onInactive
hooks:
onActive
:在通过app.setTool()
设置工具时触发。onInactive
:当其他工具替代当前工具时触发。
使用SelectTool
会很自然的通过工具切换来解决选择器和绘图工具干扰问题:
ts
const selectTool = new SelectTool({
hooks: {
onActive() {
selectorPlugin.enable();
},
onInactive() {
selectorPlugin.disable();
},
},
});
const rectTool = new RectTool({
config: {
stroke: "black",
strokeWidth: 2,
fill: "gray",
cornerRadius: 0,
opacity: 1,
},
hooks: {
onActive() {
app.cancelSelect();
},
},
});
app.setTool(selectTool);
再配合UI来触发工具切换:
html
<div ref="canvasRef" class="canvas"></div>
<div class="tools">
<button @click="app.setTool(selectTool)">选择</button>
<button @click="app.setTool(rectTool)">矩形</button>
</div>
撤销/重做
操作记录功能是一个重要的编辑器功能,Pictode
当然也支持。名为 @pictode/plugin-history
的插件,它可以为 Pictode
添加撤销和重做的能力。下面是集成 plugin-history
插件的步骤:
- 安装
@pictode/plugin-history
依赖包。
shell
npm install @pictode/plugin-selector
- 实例化
historyPlugin
插件。
ts
import { HistoryPlugin } from "@pictode/plugin-history";
const historyPlugin = new HistoryPlugin();
- 使用
app.use(historyPlugin)
方法加载插件。
ts
app.use(historyPlugin);
- 之后,你可以通过
app.undo()
和app.redo()
方法来实现撤销和重做的功能。
html
<template>
<div class="wrapper">
<div ref="canvasRef"></div>
<button @click="app.undo()">撤销</button>
<button @click="app.redo()">重做</button>
</div>
</template>
这样,用户就可以轻松地撤销和重做他们的操作,提高了编辑器的灵活性和易用性。
对齐/分布
编辑图形时,需要确保图形之间的对齐和分布。Pictode
提供了 @pictode/plugin-alignment
插件,用于实现这些功能。使用以下步骤集成插件:
- 安装
@pictode/plugin-alignment
依赖包。
shell
npm install @pictode/plugin-selector
- 实例化
alignmentPlugin
插件。
ts
import { AlignmentPlugin } from "@pictode/plugin-alignment";
const alignmentPlugin = new AlignmentPlugin();
- 使用
app.use(alignmentPlugin)
方法加载插件。
ts
app.use(alignmentPlugin);
- 之后,你可以使用一系列方法来实现对齐操作,包括
app.alignTop
、app.alignRight
、app.alignBottom
、app.alignLeft
、app.alignCenterX
、app.alignCenterY
等。 - 此外,你还可以使用
app.distributeX
和app.distributeY
方法来实现水平均分和垂直均分功能。
这些功能能够帮助你更方便地编辑和排列图形,提高了编辑效率。
目前
@pictode/*
相关的依赖包都是预发布版本,因为我正在进行自测和BUG修复中 😅😅😅
小结
通过上面的示例,你是否感受到了基于 Pictode 开发图形编辑器的简单和便捷 ?不论你使用的是 Vue、React 还是原生开发,都可以轻松集成 Pictode 到你的项目中,使定制和扩展图形编辑器变得更加灵活和容易。
架构与技术选型
Pictode旨在构建通用的图形编辑器框架。因此,核心库选择了konva.js
作为核心技术。
最初考虑了fabric.js
,但其代码结构混乱,还使用了过时的JS语法。此外,该库的下一个主要版本一直未发布,存在许多问题,如对象分组和Transformer功能。
相比之下,konva.js
代码清晰易懂,支持TS,社区活跃,并不断更新和改进。这让我最终选择konva.js
为Pictode的核心库,为编辑器提供了稳定的基础,以构建出色的图形编辑器框架。
架构设计
Pictode主要由两部分组成,一部分是编辑器的核心库与UI框架无关,另一部分是基于Vue3的使用Pictode核心库搭建的画板工具。
经过上面的DIY编辑器的过程,相信你也一定能体会到通过Pictode
DIY图形编辑器非常轻松和简单。Pictode
采用关注点分离的设计原则,使用事件驱动的方式来驱动图形编辑器的数据交互,提供了灵活的插件和图形工具机制,方便扩展和定制编辑器的能力。
⬆️这个图是用 Pictode 绘制的,虽然简陋,但能证明 Pictode 具备绘图和导出图片的能力。
项目规划
v1.0.0
v2.0.0
v3.0.0
寻求开源之路
关于是否开源的问题,答案是:肯定开源 。虽然我个人没有太多的开源经验,但我非常愿意将 Pictode 开源,以便更好地服务大家。
我正在积极准备将项目开源,但为了确保项目的开源能够既有利于开发者,又能为用户提供便捷的使用体验,还需要一些时间和努力。具体来说,我需要时间来选择适合项目的开源许可证,规范问题追踪系统和讨论区,并确保项目能够持续发展。
如果你是一位具有开源经验的掘友,并且愿意分享你的经验,帮助我将 Pictode 开源,那么请不要犹豫,随时私信我🫰。
感谢你的慷慨支持和分享精神!
如果你希望获取 Pictode 的源代码,或者关注项目的开源进展,请务必关注我的掘金账号,这样你将能第一时间获得相关信息。感谢你的关注和支持!
最后
如果你觉得 Pictode 很有趣,请不吝点赞、收藏、以及关注,你的支持对我来说意义重大,也是我坚持下去的动力。
如果你的项目需要图形编辑类的功能,而且想要使用 Pictode,请随时私信我,我会尽力提供支持。
如果你有兴趣一起参与 Pictode 的维护和发展,欢迎加入我们的学习交流群。