# 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 渲染: - **背景图片**: `` 标签 - **矩形**: `
` + CSS border - **线条**: `` + `` - **特殊标记**: `` 图标 - **文本**: `
` + 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 | `
` + border | ✅ | | 绘制线条 | Konva.Line | `` | ✅ | | 特殊标记 | Konva.Group | `` 图标 | ✅ | | 文本注释 | Konva.Text | `
` | ✅ | | 擦除工具 | layer.getIntersection() | 碰撞检测算法 | ✅ | | 撤销/重做 | 命令模式 | 命令模式 | ✅ | | 缩放 | layer.scale() | CSS transform | ✅ | | 自适应宽度 | stage.size() | CSS 百分比 | ✅ | | 触摸事件 | Konva 事件 | 原生事件 | ✅ | | 只读模式 | 不绑定事件 | 不绑定事件 | ✅ | ## 🔍 技术细节 ### 坐标转换 **Konva 方式**: ```typescript const pos = stage.getPointerPosition() const relativePos = { x: (pos.x - layer.x()) / scale - offset.x, y: (pos.y - layer.y()) / scale - offset.y, } ``` **DOM 方式**: ```typescript const rect = container.getBoundingClientRect() const relativePos = { x: (clientX - rect.left) / scale - offset.x, y: (clientY - rect.top) / scale - offset.y, } ``` ### 碰撞检测 **擦除功能的核心**:判断点击位置是否在元素内 #### 特殊标记(20x20 正方形) ```typescript 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 } ``` #### 矩形 ```typescript 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 } ``` #### 线条(点到线段距离) ```typescript 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 节点 ```typescript const rect = new Konva.Rect({ x, y, width, height }) layer.add(rect) layer.draw() ``` **DOM**: Vue 响应式自动渲染 ```vue
``` ## ✅ 测试清单 ### 基础绘制功能 - [ ] 绘制矩形 - [ ] 绘制线条(笔工具) - [ ] 添加文本注释 - [ ] 添加正确标记 ✓ - [ ] 添加错误标记 ✗ - [ ] 添加半对标记 ### 交互功能 - [ ] 擦除工具(点击擦除) - [ ] 擦除工具(拖动擦除) - [ ] 撤销操作 - [ ] 重做操作 - [ ] 清空所有标记 ### 缩放功能 - [ ] 手动缩放 - [ ] 自适应宽度模式 - [ ] 双指缩放(触摸设备) ### 快捷打分 - [ ] 快捷打分点击模式 - [ ] 加分模式 - [ ] 减分模式 - [ ] 边界检查 ### 特殊场景 - [ ] 多图片渲染 - [ ] 只读模式(历史记录查看) - [ ] 横屏/竖屏切换 - [ ] 数据导入/导出 ### 性能测试 - [ ] 大量标记渲染(100+) - [ ] 快速连续绘制 - [ ] 内存泄漏检查 ## 🐛 已知问题 ### 1. 触摸事件兼容性 **问题**: 小程序环境的触摸事件可能与浏览器不同 **解决**: 同时监听 `mousedown/touchstart` 等事件 ### 2. SVG 渲染性能 **问题**: 大量 SVG 元素可能影响性能 **优化**: - 合并相同类型的 SVG 到一个容器 - 使用虚拟滚动(如果需要) ### 3. 文本宽度估算 **问题**: 擦除功能需要估算文本宽度 **当前方案**: `width ≈ text.length * fontSize * 0.6` **改进**: 可使用 Canvas measureText 或 DOM getBoundingClientRect ## 📝 迁移步骤 ### 对于新功能 直接使用 `DomImageRenderer` 和 `useMarkingDom` ### 对于现有功能 1. 替换导入语句 2. 更新类型定义 3. 测试功能完整性 4. 删除旧的 Konva 相关代码(可选) ## 🔗 相关文件 ### 核心文件 - `src/components/marking/composables/renderer/useMarkingDom.ts` - `src/components/marking/components/renderer/DomImageRenderer.vue` - `src/components/marking/composables/renderer/useMarkingCommand.ts` ### 使用示例 - `src/components/marking/components/renderer/QuestionRenderer.vue` - `src/components/marking/components/renderer/MarkingImageViewerNew.vue` - `src/components/marking/review/ReviewImageRenderer.vue` ### 工具栏 - `src/components/marking/components/renderer/TraceToolbar.vue` ## 📚 参考资料 ### Vue 响应式渲染 - [Vue 3 响应式基础](https://cn.vuejs.org/guide/essentials/reactivity-fundamentals.html) - [Vue 3 列表渲染](https://cn.vuejs.org/guide/essentials/list.html) ### SVG 绘图 - [MDN SVG 教程](https://developer.mozilla.org/zh-CN/docs/Web/SVG/Tutorial) - [SVG Path 命令](https://developer.mozilla.org/zh-CN/docs/Web/SVG/Tutorial/Paths) ### 触摸事件 - [MDN Touch Events](https://developer.mozilla.org/zh-CN/docs/Web/API/Touch_events) - [小程序触摸事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html) ## 🎉 总结 本次迁移成功将 Konva Canvas 渲染改为 DOM + SVG 渲染,完全兼容微信小程序环境,同时保留了所有原有功能。使用 Vue 响应式系统大大简化了渲染逻辑,提高了代码可维护性。