在写一个基于 CSS Flex 来布局的思维导图

2025/04/29 | 3分钟阅读 | 更新于 2025/05/04

在写一个基于 CSS Flex 来布局的思维导图

因为注意到 XMind 的设计,同一层级的几个节点彼此对齐,间距相等。这东西和 flex 很像啊,那我为什么不也做一个思维导图工具,就拿 CSS flex 来排版呢?

核心设计:

  • 使用 CSS Flex 对节点进行布局。
  • 使用 Jotai 进行数据管理。
  • 使用 SVG path 绘制曲线。
  • 使用 getBoundingsClientRect 获取节点的位置和大小,然后在 atom 里面转换成绘图数据,用于导出图像。
  • 开始在 atom 里面存储修正过的数据,但是这样太难管理,于是后来直接存了 elements
  • 但是这样派生 atom 有时候不会触发更新,需要 atomWithRefresh
  • 文字是 div 下面 pre 和 textarea,textarea 负责输入,pre 负责撑起 div
  • flex 容器用 fit content 撑起来。

支持导出 PNG 以及 SVG:

导出的 PNG 格式的思维导图图像

Demo: Mind Map / 思维导图

源代码:bin16/flex-mind-map

更新日志

2025/04/29 更新

在写思维导图,不过嵌套的 object 树更新起来不是很方便,于是决定打平 object,用 Jotai 管理节点的数组,节点通过可选 parent id 指向上一层的节点。

然后用 atomFamily 通过 parent id 获取 children。另外搞一个派生 atom 返回 map,用来优化遍历查找 children node 的效率,使用 useMemo 缓存数据。

以及可以在 atom 里面存储 Map,必须遵守不可变更新原则,需要复制 Map 然后更新复制后的 Map 并返回。直接在原 Map 上 set 不会触发更新。这部分当然就用 immer 来简化了。

2025/05/01 更新

更新了思维导图,

  • 引入 atomWithRefresh,用来在需要的时候手动重绘
  • 在一些地方用了 immer
  • 因为很多时候需要通过 id 更改 Node,于是改成把 nodes 数据存在 Map 里面
  • 简单实现了一个 fileAtom 用于导入和导出数据
  • 添加 PouchDB 进行数据存储
  • 做了一个简单的列表页面

计划更新

  • 列表页面太丑了,要重做
  • 导出 PNG 以及 SVG
  • 拆分样式表
  • 一些加载状态的优化
  • 手动排序
  • 支持在左边创建节点
  • 同步功能
  • 修改检查 / 自动保存,考虑做个自动保存的开关,如果关掉在退出前要求确认,如果打开就每次修改自动保存
  • 撤销功能

2025/05/02 更新

完成了自动保存的功能,把保存的异步操作作为 saveAtom 的 write function,出错直接就地 set 其它 atom。然后,自动保存的核心就是当 internalNodeMapAtom 发生变更,触发 atomEffect,调用 saveAtom。考虑到每次进页面也会更新 internalNodeMapAtom,于是加了一个 autoSaveReadyAtom,初始值为 false。用户更新 nodes 是会调用几个特定函数的,在每个函数末尾都尝试更新 autoSaveReadyAtom 为 true,也就是只有当用户手动执行了一次操作后,才需要保存,这时候才会正式激活自动保存。

Jotai 这种 atom 设计其实很强,只不过不一定能提前预见到完整的条件(比如自动保存的预先检查),所以经常需要事后打补丁。

然后实现了一部分导出生成缩略图的功能,同时也是导出 PNG 的功能。原本 rectangles 就已经有每一个节点的位置和尺寸信息,也能拿到 text,所以可以直接在 canvas 上把节点画出来。然后连线可以直接用 Path2D 把 svg path 的 d 参数转换成 canvas 的路径。倒是样式,尤其圆角目前依赖 CSS,不是很统一。

实现了新的 styleMapAtom,是一个 Map 存储了 NodeStyle,增加了颜色,字号以及圆角。样式会优先通过 id 查找,然后通过其它规则查找,最后是默认样式。

2025/05/03 更新

  • 完善了一下 list view,借鉴了 XMind 的设计
  • 拆分 atom
  • 参考 XMind 修改了样式的结构
  • 重写了 svg 和 png 的导出逻辑
  • 实现了自动文字颜色
  • 给思维导图加了点状背景
  • 支持向左伸展

2025/05/04 更新

写了思维导图,完成了想要的大部分功能,用 Vercel 部署了。

© 2026 香蕉引擎故障报告

🌱 Powered by Hugo with theme Dream.

关于

要怎么介绍自己呢,🤔。

很早以前是作为 Web 前端在学习的,但是工作第一年就成为了全干工程师。喜欢尝试各种东西,什么都会一点。

一直很喜欢 Ebiten 游戏引擎 ,特别简洁,用它做过一些小东西,可以查看这个分类 。另外特别推荐这个木鱼 ,是一个相对完整的小玩意儿,包含手搓的一个简单的 UI 框架;支持鼠标和键盘操作;有多语言和主题切换功能;同时支持 Web 端和客户端。它的源代码在 bin16/wooden-fish

主题

网站基于 Hugo,当前使用的是 hugo-theme-dream 主题的修改版 ,根据我的需要,做了一些对 PaperMod 的兼容。

我自己也写过主题 ,但是没有别人写的好看。

正在从我的笔记中往外搬运内容

等待更新:

  • 从《锈湖》中学了些什么东西
  • 我拿 React 写解谜游戏的经过
  • 基于 Pocketbase 的 Pocket Memos
  • 数独!