AI + Excalidraw 自然语言绘图技术分析

一、概述

1. 项目背景

A. 核心问题

技术文档创作过程中,绘制流程图、时序图、架构图等手绘风格图表往往需要手动操作绘图工具,耗时且不直观。

B. 解决方案

结合 Excalidraw 开源手绘风格白板与大语言模型,实现通过自然语言描述自动生成手绘风格图表的工具。

2. 技术亮点

A. 流式渲染

AI 生成 JSON 元素后实时解析并渲染到画布,边生成边画图。

B. 多端适配

支持桌面端和移动端布局,移动端采用上下布局设计。

C. 数据隐私

所有画布数据和对话历史保存在浏览器 localStorage,不上传服务器。

3. 技术栈

  • 前端:Next.js + @excalidraw/excalidraw
  • 后端:Vercel AI SDK
  • AI 服务:支持 OpenAI、智谱、阿里百炼等兼容接口

二、系统架构

1. 整体架构

graph TD
    A[用户输入描述] --> B[AI 服务端]
    B --> C[流式生成 Excalidraw JSON]
    C --> D[前端流式解析器]
    D --> E[Excalidraw 画布渲染]
    F[localStorage] --> D
    F --> E

系统架构流程图

2. 组件交互

sequenceDiagram
    participant U as 用户
    participant F as 前端界面
    participant A as AI 服务
    participant E as Excalidraw 画布
    participant L as localStorage

    U->>F: 输入绘图描述
    F->>A: 发送对话请求
    A-->>F: 流式返回 JSON 元素
    F->>F: 实时解析 JSON
    loop 每解析一个元素
        F->>E: 添加元素到画布
        E-->>U: 实时渲染更新
    end
    F->>L: 保存对话历史和画布数据

组件交互时序图

三、核心技术实现

1. Excalidraw JSON 规范

A. 元素类型

Excalidraw 通过 JSON 描述图形元素,支持以下类型:

类型说明特有属性
rectangle矩形-
ellipse椭圆-
diamond菱形-
text文本text, fontSize, fontFamily
arrow箭头points, endArrowhead
line线条points

B. 基础元素结构

每个元素包含以下基础属性:

  • id:元素唯一标识
  • type:元素类型
  • x, y:位置坐标
  • width, height:尺寸
  • backgroundColor:填充颜色
  • strokeColor:边框颜色

C. 元素示例

{
  "type": "excalidraw",
  "version": 2,
  "source": "https://excalidraw.com",
  "elements": [
    {
      "id": "rect-1",
      "type": "rectangle",
      "x": 100,
      "y": 100,
      "width": 150,
      "height": 80,
      "backgroundColor": "#a5d8ff",
      "strokeColor": "#1971c2"
    }
  ]
}

2. AI 提示词设计

A. 输出格式要求

  • 先简要说明要画什么,然后连续输出所有 JSON 元素
  • 禁止在 JSON 元素之间穿插说明文字
  • 每个元素直接输出纯 JSON 对象
  • 禁止使用代码块标记(避免反引号)
  • 必须使用标准 JSON 格式

B. 文字处理规则

  • 中英文宽度计算
  • 形状内文字双向绑定
  • fontSize 和 fontFamily 设置

C. 箭头规范

  • points 使用相对坐标写法
  • 支持各方向箭头
  • endArrowhead 箭头样式设置

D. 颜色规范

使用 Excalidraw 内置调色板,确保颜色一致性。

3. 流式 JSON 解析

A. 核心挑战

AI 返回流式内容,需要:

  1. 追踪花括号深度处理嵌套 JSON
  2. 正确处理字符串内的花括号
  3. 记录已处理位置避免重复解析

B. 解析算法

function extractJsonObjects(text) {
  const results = [];
  let i = 0;

  while (i < text.length) {
    const startIndex = text.indexOf('{', i);
    if (startIndex === -1) break;

    // 追踪花括号深度
    let depth = 0;
    let inString = false;

    for (let j = startIndex; j < text.length; j++) {
      const char = text[j];

      // 处理转义字符
      if (char === '\\' && j + 1 < text.length) {
        j++;
        continue;
      }

      // 处理字符串边界
      if (char === '"') {
        inString = !inString;
        continue;
      }

      // 在字符串内不计算花括号
      if (inString) continue;

      if (char === '{') depth++;
      else if (char === '}') {
        depth--;
        if (depth === 0) {
          // 找到完整的 JSON 对象
          results.push(text.slice(startIndex, j + 1));
          i = j + 1;
          break;
        }
      }
    }
  }

  return results;
}

4. 实时渲染

A. 元素添加流程

通过 Excalidraw API 的 updateScene 方法将解析的元素添加到画布:

useImperativeHandle(ref, () => ({
  addElements: (newElements) => {
    const api = excalidrawAPIRef.current;
    const currentElements = api.getSceneElements();

    // 处理 id 冲突
    const existingIds = new Set(currentElements.map(el => el.id));
    const elementsToAdd = newElements.map(el => {
      if (existingIds.has(el.id)) {
        return { ...el, id: generateNewId() };
      }
      return el;
    });

    // 更新画布
    api.updateScene({
      elements: [...currentElements, ...elementsToAdd],
    });
  },
}));

B. ID 冲突处理

  • 检查现有元素的 ID
  • 发现冲突时生成新 ID
  • 确保每个元素唯一标识

5. 元素默认值补全

A. 降级策略

AI 可能只生成必要字段,需要补全 Excalidraw 需要的其他字段:

function getDefaultElementProps() {
  return {
    angle: 0,
    strokeColor: '#1e1e1e',
    backgroundColor: 'transparent',
    fillStyle: 'solid',
    strokeWidth: 2,
    roughness: 1,  // 手绘粗糙度
    opacity: 100,
    seed: Math.random() * 100000,  // 随机种子,产生手绘效果
    version: 1,
    versionNonce: Math.random() * 1000000000,
    isDeleted: false,
    groupIds: [],
    boundElements: null,
  };
}

B. 字段说明

  • roughness:控制手绘效果的粗糙程度
  • seed:随机种子,产生自然的手绘变化
  • opacity:透明度设置
  • groupIds:元素分组支持

四、后端服务设计

1. AI 服务兼容层

A. 多服务支持

封装统一的 AI 服务接口,支持:

  • OpenAI
  • 智谱 AI
  • 阿里百炼
  • 其他 OpenAI 兼容服务

B. 调用方式

使用 Vercel AI SDK 对接大模型,实现流式响应。

2. 安全设计

A. API 密钥保护

  • AI 调用在服务端进行
  • 不向客户端暴露 API 密钥

B. 日志记录

记录每次对话的信息:

  • IP 地址
  • User-Agent
  • 对话内容
  • 响应时间

五、前端功能设计

1. 数据持久化

A. localStorage 存储

  • 对话历史保存在 localStorage
  • 画布数据保存在 localStorage
  • 刷新页面不丢失数据

B. 隐私保护

  • 所有数据仅存储在浏览器本地
  • 不上传到服务器
  • 用户完全控制自己的数据

2. 移动端适配

A. 布局设计

  • 桌面端:左右布局
  • 移动端:上下布局(画布在上方,输入框在底部)

B. 功能限制

移动端核心功能:

  • AI 生成图表
  • 查看渲染效果
  • 基础画布操作

六、实现方式对比

1. AI 辅助实现

A. 适用场景

  • 已清楚实现思路
  • 需要快速原型
  • 减少重复劳动

B. 操作方式

将详细需求整理为 prompt,使用 AI IDE 的 Plan 模式生成代码。

C. 优缺点

优点:

  • 开发速度快
  • 核心逻辑可靠

缺点:

  • 对实现细节了解不深
  • 可能需要调整代码

2. 手动实现

A. 适用场景

  • 学习技术思路
  • 理解实现细节
  • 掌握核心技术

B. 学习价值

  • 深入理解流式解析
  • 掌握 Excalidraw API
  • 熟悉 AI 集成方案

七、技术评估

1. 当前状态

A. 技术可行性

流程已打通,核心功能可用。

B. 实际效果

作者评价:流程是通的,效果是拉胯的。

C. 应用定位

目前仅适合作为玩具体验,距离生产可用还有差距。

2. 技术挑战

A. AI 生成质量

  • JSON 格式准确性
  • 元素布局合理性
  • 箭头连接准确性

B. 复杂图表支持

  • 大规模元素渲染
  • 元素关系自动布局
  • 美观性优化

3. 改进方向

A. 提示词优化

  • 更精确的格式约束
  • 更好的布局指导
  • 更丰富的示例

B. 后处理增强

  • 自动布局算法
  • 元素对齐优化
  • 美观度调整

C. 多轮对话

  • 支持修改已有图表
  • 增量添加元素
  • 智能调整布局

八、相关资源

1. 官方文档

2. 技术栈

  • Next.js:React 框架
  • @excalidraw/excalidraw:Excalidraw 官方包
  • Vercel AI SDK:AI 集成工具

3. 在线体验


参考资料

  1. AI+Excalidraw,用自然语言画手绘风格技术图
最后修改:2026 年 01 月 15 日
如果觉得我的文章对你有用,请随意赞赏