游客发表

vue实现思维导图功能

发帖时间:2026-10-17 04:36:21

用 Vue 做思维导图,现思核心不是维导“画图”,而是图功把树形数据映射成可交互的节点坐标系。我去年给一个内部知识库项目加脑图功能,现思试过 jsPlumb、维导G6,图功最后回到最朴素的现思 SVG + 递归组件,反而最稳。维导原因很简单:思维导图的图功布局算法比渲染本身更关键,Vue 的现思响应式刚好适合处理“数据变→节点位置重算→连线更新”这条链路。

先定数据结构。维导一个节点至少要有 id、图功label、现思children,维导外加可选的图功 collapsed 状态。别急着上拖拽,先让静态树能正确排布。我用的是一种简化版 Reingold-Tilford 思路:后序遍历算出每棵子树的高度,父节点纵向居中于子节点区间。代码不长,但递归里要传一个可变的 y 游标,Vue 的 setup 里用普通对象包一层就行,不需要 ref,因为布局计算是同步的。

function layout(node, depth, cursor) {  node.x = depth * 220; if (!node.children || node.collapsed) {  node.y = cursor.y; cursor.y += 80; return; } const start = cursor.y; node.children.forEach(c => layout(c, depth + 1, cursor)); node.y = (start + cursor.y - 80) / 2; } 

渲染层用递归组件 MindNode,每个节点画一个 <g>,里面放 <rect> 和 <text>。连线单独抽一个 MindLink,用三次贝塞尔曲线,控制点取父子节点水平中点。这里有个细节:Vue 的 v-for 直接遍历扁平化后的节点数组比递归渲染更利于做动画,因为 key 稳定,增删节点时过渡自然。我一开始用递归,折叠时整棵树闪一下,换成扁平列表加 <transition-group> 就顺了。

交互部分只做三件事:点击折叠、双击编辑、拖拽平移画布。折叠就是改 collapsed 布尔值,然后重新跑 layout。编辑用 contenteditable 的 <foreignObject> 嵌在 SVG 里,比弹窗体验好。平移监听 mousedown 记录起始坐标,mousemove 时更新一个 transform 字符串,直接绑在 <g> 上。缩放用滚轮改 scale,注意要以鼠标位置为原点,不然会飘。

性能上,节点超过 200 个就要做视口裁剪。我的做法是在 transform 更新时算一下当前可视区域,只渲染落在区域内的节点和连线。Vue 的 computed 在这里很好用,依赖 scale、translate 和节点坐标,自动缓存。另外别用 watch 深度监听整棵树,改成在修改节点时手动触发 layoutVersion++,省掉大量无谓计算。

最后说个踩过的坑:SVG 里 text 的换行很麻烦,tspan 要手动算宽度。我干脆限制标签不超过 12 个字符,超出用省略号,鼠标悬停显示完整内容。思维导图不是富文本编辑器,节点文字短一点,布局反而更清晰。整套代码不到 400 行,没有依赖任何图形库,打包后 gzip 也就 8KB。如果你只是要个能折叠、能拖拽的脑图,这个方案比引入 G6 再定制省事得多。

    热门排行

    友情链接