xlx_teacher_app/.task/konva-to-dom-migration.md

7.3 KiB
Raw Permalink Blame History

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

📝 迁移步骤

对于新功能

直接使用 DomImageRendereruseMarkingDom

对于现有功能

  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 响应式渲染

SVG 绘图

触摸事件

🎉 总结

本次迁移成功将 Konva Canvas 渲染改为 DOM + SVG 渲染,完全兼容微信小程序环境,同时保留了所有原有功能。使用 Vue 响应式系统大大简化了渲染逻辑,提高了代码可维护性。