7.3 KiB
7.3 KiB
Konva 到 DOM 渲染迁移文档
📋 迁移概述
迁移原因
微信小程序不支持 Konva.js,需要将基于 Canvas 的渲染方案改为纯 DOM + SVG 渲染方案。
迁移目标
- ✅ 保留所有现有功能
- ✅ 使用 Vue 响应式系统管理渲染
- ✅ 保持命令模式的撤销/重做功能
- ✅ 兼容微信小程序环境
🔄 核心变更
1. 新增文件
useMarkingDom.ts
替代 useMarkingKonva.ts,使用 DOM 渲染:
- 数据结构:
DomMarkingData(兼容原KonvaMarkingData) - 工具枚举:
MarkingTool(保持不变) - 核心函数:
useSimpleDomLayer替代useSimpleKonvaLayer
主要特性:
- 使用 Vue 响应式系统自动渲染
- 鼠标/触摸事件处理
- 坐标转换(支持缩放和偏移)
- 碰撞检测(用于擦除功能)
DomImageRenderer.vue
替代 KonvaImageRenderer.vue,使用 DOM 渲染:
- 背景图片:
<img>标签 - 矩形:
<div>+ CSS border - 线条:
<svg>+<polyline> - 特殊标记:
<svg>图标 - 文本:
<div>+ CSS
优势:
- 无需 Canvas API
- 完全兼容小程序
- Vue 自动管理 DOM 更新
- 更好的性能(小数据量场景)
2. 修改文件
useMarkingCommand.ts
- 将类型从
KonvaMarkingData改为BaseMarkingData - 将
KonvaShape改为BaseShape - 移除对
layer的依赖(传入null) - 保持命令模式逻辑不变
QuestionRenderer.vue
- 导入
DomImageRenderer替代KonvaImageRenderer - 导入
useMarkingDom类型替代useMarkingKonva - 其他逻辑保持不变
MarkingImageViewerNew.vue
- 更新类型导入:
DomMarkingData替代KonvaMarkingData
ReviewImageRenderer.vue
- 使用
DomImageRenderer替代KonvaImageRenderer - 只读模式渲染历史标记
TraceToolbar.vue
- 更新导入:从
useMarkingDom导入MarkingTool
3. 保留文件(兼容性)
KonvaImageRenderer.vue
保留但不再使用,可用于对比测试
useMarkingKonva.ts
保留但不再使用,可用于对比测试
useMarkingTools.ts
标记为 @deprecated,仅用于 Konva 兼容
🎯 功能对照表
| 功能 | Konva 实现 | DOM 实现 | 状态 |
|---|---|---|---|
| 绘制矩形 | Konva.Rect | <div> + border |
✅ |
| 绘制线条 | Konva.Line | <svg><polyline> |
✅ |
| 特殊标记 | Konva.Group | <svg> 图标 |
✅ |
| 文本注释 | Konva.Text | <div> |
✅ |
| 擦除工具 | layer.getIntersection() | 碰撞检测算法 | ✅ |
| 撤销/重做 | 命令模式 | 命令模式 | ✅ |
| 缩放 | layer.scale() | CSS transform | ✅ |
| 自适应宽度 | stage.size() | CSS 百分比 | ✅ |
| 触摸事件 | Konva 事件 | 原生事件 | ✅ |
| 只读模式 | 不绑定事件 | 不绑定事件 | ✅ |
🔍 技术细节
坐标转换
Konva 方式:
const pos = stage.getPointerPosition()
const relativePos = {
x: (pos.x - layer.x()) / scale - offset.x,
y: (pos.y - layer.y()) / scale - offset.y,
}
DOM 方式:
const rect = container.getBoundingClientRect()
const relativePos = {
x: (clientX - rect.left) / scale - offset.x,
y: (clientY - rect.top) / scale - offset.y,
}
碰撞检测
擦除功能的核心:判断点击位置是否在元素内
特殊标记(20x20 正方形)
const isPointInSpecialMark = (pos, mark) => {
const size = 20
return pos.x >= mark.x - size/2 && pos.x <= mark.x + size/2
&& pos.y >= mark.y - size/2 && pos.y <= mark.y + size/2
}
矩形
const isPointInRect = (pos, rect) => {
return pos.x >= rect.x && pos.x <= rect.x + rect.width
&& pos.y >= rect.y && pos.y <= rect.y + rect.height
}
线条(点到线段距离)
const isPointInLine = (pos, line) => {
const threshold = strokeWidth + 5
for (let i = 0; i < points.length - 2; i += 2) {
const distance = pointToLineDistance(pos, p1, p2)
if (distance < threshold) return true
}
return false
}
渲染方式
Konva: 手动管理 Konva 节点
const rect = new Konva.Rect({ x, y, width, height })
layer.add(rect)
layer.draw()
DOM: Vue 响应式自动渲染
<div
v-for="shape in markingData.shapes"
:key="shape.id"
:style="{
left: `${shape.x}px`,
top: `${shape.y}px`,
width: `${shape.width}px`,
height: `${shape.height}px`,
}"
/>
✅ 测试清单
基础绘制功能
- 绘制矩形
- 绘制线条(笔工具)
- 添加文本注释
- 添加正确标记 ✓
- 添加错误标记 ✗
- 添加半对标记
交互功能
- 擦除工具(点击擦除)
- 擦除工具(拖动擦除)
- 撤销操作
- 重做操作
- 清空所有标记
缩放功能
- 手动缩放
- 自适应宽度模式
- 双指缩放(触摸设备)
快捷打分
- 快捷打分点击模式
- 加分模式
- 减分模式
- 边界检查
特殊场景
- 多图片渲染
- 只读模式(历史记录查看)
- 横屏/竖屏切换
- 数据导入/导出
性能测试
- 大量标记渲染(100+)
- 快速连续绘制
- 内存泄漏检查
🐛 已知问题
1. 触摸事件兼容性
问题: 小程序环境的触摸事件可能与浏览器不同
解决: 同时监听 mousedown/touchstart 等事件
2. SVG 渲染性能
问题: 大量 SVG 元素可能影响性能 优化:
- 合并相同类型的 SVG 到一个容器
- 使用虚拟滚动(如果需要)
3. 文本宽度估算
问题: 擦除功能需要估算文本宽度
当前方案: width ≈ text.length * fontSize * 0.6
改进: 可使用 Canvas measureText 或 DOM getBoundingClientRect
📝 迁移步骤
对于新功能
直接使用 DomImageRenderer 和 useMarkingDom
对于现有功能
- 替换导入语句
- 更新类型定义
- 测试功能完整性
- 删除旧的 Konva 相关代码(可选)
🔗 相关文件
核心文件
src/components/marking/composables/renderer/useMarkingDom.tssrc/components/marking/components/renderer/DomImageRenderer.vuesrc/components/marking/composables/renderer/useMarkingCommand.ts
使用示例
src/components/marking/components/renderer/QuestionRenderer.vuesrc/components/marking/components/renderer/MarkingImageViewerNew.vuesrc/components/marking/review/ReviewImageRenderer.vue
工具栏
src/components/marking/components/renderer/TraceToolbar.vue
📚 参考资料
Vue 响应式渲染
SVG 绘图
触摸事件
🎉 总结
本次迁移成功将 Konva Canvas 渲染改为 DOM + SVG 渲染,完全兼容微信小程序环境,同时保留了所有原有功能。使用 Vue 响应式系统大大简化了渲染逻辑,提高了代码可维护性。