Skip to content

思维导图

本页介绍思维导图模块的设计和实现。

数据结构

typescript
interface MindMapNode {
  id: string
  text: string
  x: number
  y: number
  color?: string
  parentId?: string
  relatedSentenceIds?: string[]
}

interface MindMapData {
  nodes: MindMapNode[]
  articleId: string
  createdAt: number
  updatedAt: number
}

Canvas 渲染

绘制节点

typescript
function drawNode(ctx: CanvasRenderingContext2D, node: MindMapNode) {
  // 绘制圆形背景
  ctx.beginPath()
  ctx.arc(node.x, node.y, 30, 0, Math.PI * 2)
  ctx.fillStyle = node.color || '#4ECDC4'
  ctx.fill()
  
  // 绘制文字
  ctx.fillStyle = 'white'
  ctx.font = '14px sans-serif'
  ctx.textAlign = 'center'
  ctx.textBaseline = 'middle'
  ctx.fillText(node.text, node.x, node.y)
}

绘制连线

typescript
function drawLine(
  ctx: CanvasRenderingContext2D,
  parent: MindMapNode,
  child: MindMapNode
) {
  ctx.beginPath()
  ctx.moveTo(parent.x, parent.y)
  ctx.lineTo(child.x, child.y)
  ctx.strokeStyle = '#ccc'
  ctx.lineWidth = 2
  ctx.stroke()
}

交互处理

拖拽

typescript
function handleDrag(canvas: HTMLCanvasElement, nodes: MindMapNode[]) {
  let draggedNode: MindMapNode | null = null
  let offsetX = 0, offsetY = 0
  
  canvas.addEventListener('mousedown', (e) => {
    const { x, y } = getMousePos(canvas, e)
    draggedNode = findNodeAt(nodes, x, y)
    if (draggedNode) {
      offsetX = x - draggedNode.x
      offsetY = y - draggedNode.y
    }
  })
  
  canvas.addEventListener('mousemove', (e) => {
    if (draggedNode) {
      const { x, y } = getMousePos(canvas, e)
      draggedNode.x = x - offsetX
      draggedNode.y = y - offsetY
      render()
    }
  })
  
  canvas.addEventListener('mouseup', () => {
    draggedNode = null
  })
}

缩放和平移

typescript
let scale = 1
let offsetX = 0, offsetY = 0

canvas.addEventListener('wheel', (e) => {
  e.preventDefault()
  const delta = e.deltaY > 0 ? 0.9 : 1.1
  scale *= delta
  render()
})

导出功能

导出为图片

typescript
async function exportAsImage(): Promise<Blob> {
  const canvas = document.getElementById('mindmap-canvas')
  return new Promise((resolve) => {
    canvas.toBlob(resolve, 'image/png')
  })
}

导出为 JSON

typescript
function exportAsJSON(): string {
  return JSON.stringify(mindMapData, null, 2)
}

核心 API

typescript
class MindMapService {
  // 节点操作
  createNode(text: string, x: number, y: number): MindMapNode
  updateNode(id: string, updates: Partial<MindMapNode>): void
  deleteNode(id: string): void
  
  // 关系操作
  setParent(nodeId: string, parentId: string): void
  removeParent(nodeId: string): void
  
  // 句子关联
  linkSentence(nodeId: string, sentenceId: string): void
  unlinkSentence(nodeId: string, sentenceId: string): void
  
  // 导出
  exportAsImage(): Promise<Blob>
  exportAsJSON(): string
  
  // 保存加载
  save(articleId: string): void
  load(articleId: string): MindMapData | null
}

基于 MIT 许可发布