画布式锚点布局 — 正文固定 + 节点散布 + 连线关联

保留 React Flow 画布交互,正文为不可移动但可缩放的中央容器,节点通过连线关联正文锚点

备课编辑器 — 画布视图(默认状态)
📖 秋天(第一课时) 语文 · 一年级上册
💾 已保存 📋 版本 💾 保存
📜 课文正文 🔒 固定
秋天

天气凉了,树叶黄了,

天空那么蓝,那么高。

一群大雁往南飞,

一会儿排成个"人"字

一会儿排成个"一"字

啊!秋天来了!

🔍
1 💡 导入
提问:你见过秋天的树叶吗?
2 📝 文本研习
赏析:叠词的运用
3 📚 新授
讲解:大雁南飞
4 ✏️ 练习
3 道题
5 📌 小结
总结全文
🎯 教学目标
⭐ 重难点
🏠 作业
📋 板书设计
💭 教学反思
+
+ 添加节点
连线默认 10% 透明度 · 选中节点时完整显示

选中节点时的连线显示对比

默认状态 — 连线 10% 透明度
导入 课文
连线几乎不可见,画布干净
选中"导入"节点 — 连线 100% 显示
导入 课文
连线完整显示,高亮锚点位置

数据模型设计

// 正文容器节点(特殊节点类型,不可拖动,可缩放)
interface TextbookContentNode extends LessonPlanNode {
  type: "textbook_content"; // 新增节点类型
  data: {
    chapterId: string; // 关联教材章节
    content: string; // Markdown 正文(缓存)
    zoom: number; // 缩放比例 0.5-2.0
  };
  position: { x: number; y: number }; // 固定位置(不可拖动)
  draggable: false; // React Flow 节点锁定
}

// 锚点连线(节点 → 正文位置)
interface AnchorEdge extends LessonPlanEdge {
  type: "anchor"; // 锚点连线(vs "flow" 流程连线)
  source: string; // 节点 ID
  target: string; // 正文节点 ID
  targetHandle: string; // "anchor:123:145"(正文偏移量 start:end)
}

// LessonPlanDocument v3
interface LessonPlanDocument {
  version: 3;
  nodes: LessonPlanNode[]; // 含 1 个 textbook_content + N 个教学节点
  edges: AnchorEdge | FlowEdge[]; // 锚点连线 + 流程连线
}

核心交互流程

🔗 锚定节点到正文

  1. 教师选中正文某段文字(或某个字)
  2. 选中后弹出浮动菜单:"关联节点 →"
  3. 从下拉列表选择已有节点,或"新建节点"
  4. 创建 AnchorEdge,source=节点, target=正文节点, targetHandle="anchor:start:end"
  5. 正文对应文字高亮显示(节点颜色)
  6. 连线默认 10% 透明,选中节点时 100%

🖱️ 拖动节点到正文

  1. 教师从右侧节点列表拖动一个节点
  2. 拖动过程中,正文区域高亮可放置区域
  3. 拖到正文某个字前释放
  4. 创建点锚点(point anchor),targetHandle="anchor:pos"
  5. 节点自动定位到正文旁边(左或右空位)
  6. 连线默认 10% 透明,选中时完整显示

设计要点

优势

  • 保留画布交互:缩放/平移/拖动节点,与当前备课模块一致
  • 正文固定居中:不可拖动,始终是视觉中心
  • 连线语义化:anchor 锚点连线 vs flow 流程连线
  • 透明度策略:默认 10%,选中时 100%,画布不杂乱
  • 正文可缩放:教师可放大正文便于阅读

技术挑战

  • 正文偏移量需基于纯文本(Markdown 渲染后映射)
  • 正文内容变更后锚点需重新定位
  • React Flow 自定义节点需处理正文渲染
  • 数据结构升级 v2 → v3,需迁移