实现一个简单的 React 脑图组件

简介

主要记录本人在实现一个简单的 React 脑图展示组件过程中的思路和设计,具体代码开源在我的 github

一个简单的脑图展示组件,包含以下功能:

  • 树形数据
  • 横向自动布局
  • 节点样式、内容、事件的自定义
  • 直线/曲线连接
  • 画布支持拖拽缩放
  • 节点支持折叠展开

功能拆分

一个思维导图主要由以下部分组成:

  • 画布
    • 拖拽
    • 缩放
  • 节点
    • 布局
    • 样式
    • 折叠展开
    • 自定义程度
  • 线
    • 连接
    • 样式

整体的思路是利用普通DOM来渲染节点,用SVG来渲染连线,用D3.js来辅助SVG的渲染和拖动缩放操作。

接下来分别讲下对应部分的大致思路与方案。

画布

画布分为2块,一块是窗口 viewport ,这部分一般限制宽高,使用 overflow: hidden 做成窗口的样式;另一块则是容器 container,这部分中容纳所有的节点与连线,在 container 上通过 translate 来实现画布整体的平移与缩放。

画布可以被抽象成一个以左上角为原点的坐标系,节点在布局后定位到这个坐标系上。

节点

节点渲染比较复杂的地方在于布局:如何将节点按照树形的结构排列放置到画布上。

这部分参考公开发表的算法:树结构自动布局算法,该算法最终会获得一颗垂直的非分层布局树

分层树:平级的兄弟节点在水平上对齐,一层的高度为最高的那个兄弟节点的高度决定,父子节点的间距不固定

非分层树:平级的兄弟节点在水平上是不对齐的,子节点会尽可能的占据空间,父子节点的间距固定

有了布局算法,当传入的原始数据 tree data 就会转化为带有坐标轴位置的 tree data。

对了,布局算法需要明确节点的宽高,但是原始数据只有节点内容,是不固定的,这里我的方案是先让节点渲染一遍,但是是隐藏的 (visbility: hidden),这样获取到节点元素的宽高后再进行布局。此外,将节点的渲染函数作为组件属性传入到组件内部,就可以最大程度自定义节点。

节点的折叠和展开:1子节点的显隐;2子节点所有连线的显隐。这二者直接通过样式的显隐去控制。

连线

连线在有了 D3.js 的辅助后,就转化为了找到需要连接的两个点的坐标。在上面布局完成后,节点的坐标加上节点的宽高,那么父子节点的连接点不难计算出。

代码结构

我将代码逻辑分成以下部分:

  • Core 核心逻辑,主要负责数据存储、布局逻辑封装、流程编排、连线函数、API暴露
  • View 视图逻辑,主要负责React封装、渲染、画布/节点事件
  • 布局算法,原布局算法是按自顶向下排列树节点,这里需要转换成横向排列

整体执行流程图

flowchart TD

start[初始化画布,左上角为坐标轴原点] --> B(传入树形结构数据和自定义渲染函数)

B --> C(执行自定义渲染函数,渲染节点但是设为隐藏,获取到节点宽高,存入树形结构数据)

C --> D(使用非分层树布局算法计算节点左上角在坐标轴的位置)

D --> E(修改第三步中隐藏节点的位置,将隐藏改为可见)

E --> F(计算父子节点连线的两个点的位置,使用D3.js提供的API连接两个点)

F --> G[思维导图渲染完成]