实现一个简单的 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[思维导图渲染完成]