基于Vue3响应式系统与SVG矢量图,实现零外部依赖的AI脑图生成器。通过解析Markdown大纲文本为嵌套树结构,递归计算节点坐标并映射为SVG元素,支持平移缩放与节点折叠。采用文本截断与CSS硬件加速优化长文本和移动端性能,代码简洁高效。

独立开发这件事,说到底就是用最轻巧的手段,去死磕一个最具体的痛点。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
有时候脑子里突然蹦出个点子,特别想赶紧梳理成思维导图。可翻一圈市面上的脑图工具,一个比一个臃肿,导出功能还要收费。于是我们想,能不能先让AI帮忙理清思路,自动输出Markdown格式的层级大纲,然后前端只用百来行Vue 3代码,用SVG直接画出来?
这就是这次折腾出来的结果——一个零外部重度依赖、能平移缩放、代码干净得像诗一样的AI脑图渲染器。下面就把这套方案拆开讲讲。
整个流程可以拆成两大块:大纲文本解析和树状排版渲染。
graph TDA["AI 输出 Markdown 大纲文本"] --> B["大纲语法解析器 (Parser)"]B --> C["嵌套树状 Ja vaScript 对象"]C --> D["Vue 3 响应式计算: 动态计算每个节点的 X/Y 坐标"]D --> E["SVG 画板渲染"]E --> F["节点元素 ( )"]E --> G["贝塞尔曲线连接线 ()"]
这里面有几个关键环节:
children数组的嵌套树结构。的二次贝塞尔曲线连起来,干净利落。| 对比维度 | Canvas 画布渲染 | 纯 SVG 矢量图渲染 |
|---|---|---|
| 代码量与开发体验 | 复杂(需要手写大量清除和重绘逻辑) | 极少(利用 Vue 数据绑定直接映射,逻辑简单) |
| 交互与事件绑定 | 麻烦(需要手动计算点击的像素坐标范围) | 极易(直接在元素上写 @click 或 @mousedown) |
| 文字渲染效果 | 容易变模糊(受限于物理分辨率) | 完美矢量缩放(无级放大依然清晰) |
| 导出能力 | 直接导出 PNG 较快 | 可以方便地直接导出标准 .svg 文本 |
先定义脑图节点在前端的TypeScript类型。
// 定义脑图节点结构interface 脑图节点 {唯一标识: string;文字内容: string;深度: number;子节点列表: 脑图节点[];展开状态: boolean;}
写一个简单的纯函数,把大模型返回的带缩进列表解析成上面的树形结构。
export function 解析大纲文本(大纲文本: string): 脑图节点 {const 行列表 = 大纲文本.split('').filter(行 => 行.trim().startsWith('-'));const 根节点: 脑图节点 = { 唯一标识: '0', 文字内容: '核心主题', 深度: 0, 子节点列表: [], 展开状态: true };const 栈: 脑图节点[] = [根节点];行列表.forEach((行, 索引) => {// 1. 计算当前行的缩进空格数const 缩进空格 = 行.search(/S/);const 节点文字 = 行.replace(/^s*-s*/, '').trim();// 2. 根据空格估算深度(假设每次缩进 2 空格)const 当前深度 = Math.floor(缩进空格 / 2) + 1;const 新节点: 脑图节点 = {唯一标识: `节点-${索引}-${Date.now()}`,文字内容: 节点文字,深度: 当前深度,子节点列表: [],展开状态: true};// 3. 寻找其父节点while (栈.length > 0 && 栈[栈.length - 1].深度 >= 当前深度) {栈.pop();}if (栈.length > 0) {栈[栈.length - 1].子节点列表.push(新节点);}栈.push(新节点);});return 根机制优化(根节点);}// 辅助清洗函数function 根机制优化(根: 脑图节点): 脑图节点 {if (根.子节点列表.length === 1 && 根.文字内容 === '核心主题') {return 根.子节点列表[0];}return 根;}
核心难点是计算节点的X与Y物理坐标。为了让布局好看,我们在Y轴上进行等距撑开,在X轴上按层级递增。
interface 坐标节点 extends 脑图节点 {x: number;y: number;}// 递归计算每个节点渲染时的坐标export function 动态布局节点(当前节点: 脑图节点, 起点X: number, 起点Y: number, 层级宽度: number, 行高: number): { 布局结果: 坐标节点; 分支总高度: number } {const 坐标实例 = { ...当前节点, x: 起点X, y: 起点Y } as 坐标节点;if (!当前节点.展开状态 || 当前节点.子节点列表.length === 0) {return { 布局结果: 坐标实例, 分支总高度: 行高 };}let 子分支累计高度 = 0;const 渲染子节点列表: 坐标节点[] = [];// 计算子节点的相对 Y 坐标偏移量当前节点.子节点列表.forEach((子节点, 索引) => {const 子节点目标Y = 起点Y + 子分支累计高度;const { 布局结果: 子布局, 分支总高度: 子高度 } = 动态布局节点(子节点,起点X + 层级宽度,子节点目标Y,层级宽度,行高);子分支累计高度 += 子高度;渲染子节点列表.push(子布局);});// 父节点在 Y 轴上应该居中对齐所有子分支const 子节点首个Y = 渲染子节点列表[0].y;const 子节点末尾Y = 渲染子节点列表[渲染子节点列表.length - 1].y;坐标实例.y = (子节点首个Y + 子节点末尾Y) / 2;// 将校准后的子节点重新挂回坐标实例.子节点列表 = 渲染子节点列表;return { 布局结果: 坐标实例, 分支总高度: 子分支累计高度 };}
把计算出来的节点数组直接映射到SVG画布上,连线和点击折叠一气呵成。
问题表现:SVG中的标签不支持CSS的word-break自动折行。如果大模型输出的节点内容过长,文字会直截了当地超出卡片矩形边框。
解决方案:在Vue中写一个文本截断滤镜,当字数超出8个字时自动截断,并添加...:
const 过滤文字 = (内容: string) => 内容.length > 8 内容.slice(0, 7) + '...' : 内容;
性能瓶颈:随着脑图节点数目突破50个,频繁点击折叠会引起SVG频繁重绘,在手机端可能出现帧率暴跌。
解决方案:为外层的容器加上CSS 3硬件加速样式transform: translate3d(0,0,0);,强迫浏览器启用显卡硬件图层合成,实现高帧率渲染。
好的独立产品,交互逻辑应该一眼见底。
利用Vue 3的响应式数据系统去驱动原生的SVG画布,免去了传统命令式画布框架中复杂的清理重画逻辑。整个脑图解析和绘制仅仅使用了几十行干净的代码,这就是极简工程美学。
希望这套干净的方案,能为你下一个AI独立产品的变王见之路打下好基础。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述