需求评估
最近接到一个需求是在项目中实现一个功能和微信图片编辑功能类似的组件,但是 UI 比较自定义化,遍寻了市面上的轮子可能不太好实现这样的自定义功能,于是决定自己造一个轮子,顺便学习下 rollup 打包组件这套流程。
首先我选择了
konvajs
这个 canvas 操作库来实现图片编辑的功能,具体考察了下 konvajs
的功能和文档发现还是比较靠谱的。然后需要实现一个历史记录栈的功能使用 stateshot
这个库来实现。我们需要实现的功能主要有画笔,添加文字,裁切图片和图片打码。这几个功能里裁切和打码是比较有难度的,其他的基于
konvajs
来实现都比较简单。组件结构设计
因为一开始是想设计一个和微信裁切功能类似的 React 组件,所以就拥抱 React 生态,设计了很多层的 Context 来分割代码。
<EditorProvider> <HistoryProvider> <EditorStage /> </HistoryProvider> </EditorProvider>
EditorProvider 主要用来放置系统性的配置,如图片尺寸和画笔初始化粗细等参数。
HistoryProvider 用来保存整个系统的数据和历史记录,并向全局暴露操作方法。
EditorStage 是主要的操作区域,用来渲染 UI 和操作
konvajs
,主要由以下几部分组成:<Stage> <Layer> <Group> // 操作层 <Group> // 裁切层 <Image/> // 实际渲染图片 <Line/> // 画笔 <Text/> // 文字 </Group> <Transfromer/> // 文字操作Transfromer </Group> </Layer> </Stage> <ClipStage/> // 裁切操作 <Toolbar/> // 工具栏
- 主 Stage 只负责展示和实现除裁切之外的功能,操作层主要提现图片的放大,旋转和位移等参数。
- 裁切层主要体现对图片的裁切等操作。
- ClipStage中主要实现对图片的裁切。
数据结构设计
type HistoryType = { image: image, group: { width: number, // 图片实际宽度 height: number, // 图片实际高度 x: number, // 图片 x 坐标 y: number, // 图片 y 坐标 rotation: number, // 图片旋转角度 90 的倍数 }, clipRect: { width: number, // 图片裁剪区域宽度 height: number, // 图片裁剪区域高度 x: number, // 图片裁剪区域相对于左上角的 x 轴的距离 y: number, // 图片裁剪区域相对于左上角的 y 轴的距离 }, pencils: { stroke: color, strokeWidth: number, scale: 1 / scaleRatio, // 画笔的放大系数 x: number, // 图片的实际 x 位移 / scaleRatio y: number, // 图片的实际 y 位移 / scaleRatio points: number[], // 绘制坐标点 }[], texts: { fontSize: number, fill: color, width: number, scale: 1 / scaleRatio, // 文字的放大系数 x: number, // 当前视觉中心相对于图片左上角的 x 轴的距离 y: number, // 当前视觉中心相对于图片左上角的 y 轴的距离 text: string, }[] }
主要思路
一开始我对裁切的思路是每一次裁切后就对裁切区域生成一张新的图片,但是在历史记录栈中会存储很多图片,占用内存过大,而且裁切后的文字内容也不可再操作。
于是后面改为只记录裁切区域在图片中的坐标和大小,从而根据容器大小来计算将裁切中心方法到适合容器尺寸并位移到容器中心的位移,使用这个位移和放大系数来操作操作层,并使用裁切的坐标来操作裁切层。