AI+Excalidraw,用自然语言画手绘风格技术图

✨前言

Hi,我是松柏!

不知道大家有没有用过 Excalidraw ,Excalidraw 是一款开源的手绘风格虚拟白板,在 Github 上有 113k 的 star,画出来的流程图、时序图、架构图等等还是挺有特色的,大概是这种风格:

最近我偶然了解到 Excalidraw 的原理是通过 JSON 绘图,那岂不意味着可以通过 AI 生成符合规范的 JSON 内容来实现自动绘图!所以,我就做了个工具,实现通过 AI 自动绘制手绘风格图,实时流式渲染,边生成边画图:

这还有个 gif 版的,虽然有点糊,不过能看出是流式渲染的:

移动端的效果:

体验地址会放到文章底部,废话不多说,点赞关注,我们直接发车!

AI 实现

这里给大家分享两种实现方式,一种是通过 AI 实现,我们只负责说清楚需求,然后作为一个监工让 AI 干活,这也是我现在做新功能最喜欢的方式;另一种就是一步一步的手动编码实现啦。

不过第一种方式只适合我们已经清楚了这个应该怎么做,有思路但是不想做一些重复的劳动,如果是为了学习的话,还是更建议自己实现一遍。

先来看怎么让 AI 来做这个需求吧。我们要做的就是把需求整理成一个详细的 prompt,直接复制给 AI 即可:

plain 复制代码
帮我实现一个 AI + Excalidraw 手绘风格绘图工具,具体要求如下:

## 一、功能需求
1. 集成 Excalidraw 画板,参考官方文档:https://docs.excalidraw.com/docs/@excalidraw/excalidraw/integration
2. 通过 AI 对话生成 Excalidraw JSON 格式的图形元素
3. 流式输出 + 实时渲染:每解析到一个完整的 JSON 元素就立即渲染到画布
4. 支持移动端和桌面端布局

## 二、后端要求
1. AI 调用在服务端进行,不暴露 API 密钥
2. 记录每次对话的 IP、User-Agent、对话内容、响应时间等信息
3. 支持灵活切换各家 AI 服务(OpenAI、智谱、阿里百炼等),封装统一的兼容层

## 三、前端要求
1. 对话历史保存在 localStorage,不依赖后端加载
2. 画布数据也保存在 localStorage,刷新不丢失
3. 流式解析 AI 返回的 JSON 元素,处理嵌套和字符串内的花括号
4. 为 AI 生成的元素补全 Excalidraw 必需的默认字段

## 四、代码质量
1. 组件封装:AI 服务层、流式解析器、画布组件、对话组件分离
2. 类型安全:使用 TypeScript,定义完整的类型
3. 可复用:AI 服务封装成可配置的模块

## 五、AI 绘图提示词要点
需要设计一个 System Prompt,包含:
- 输出格式:纯 JSON,禁止代码块,方便流式解析
- 文字处理:中英文宽度计算、形状内文字双向绑定
- 箭头规范:points 相对坐标写法
- 常用颜色:Excalidraw 内置调色板

最好是用一些 AI IDE 的 Plan 模式,像 Cursor 、Trae 这些,先规划,再执行。

当然了,AI 生成的代码可能需要根据实际情况再进行调整,但核心逻辑一般都是没问题的。这种方式的好处是快,非常快,原本三个小时的活三分钟 AI 就搞完了;缺点就是我们对具体的实现细节可能不那么了解。

手动实现

如果是以学习思路为目的,更推荐用这种方式实现。

整体架构

整个工具的架构分为三层:

plain 复制代码
[用户输入描述]
      ↓
[AI 流式生成 Excalidraw JSON]
      ↓
[流式解析 JSON 元素]
      ↓
[实时渲染到 Excalidraw 画布]

核心思路是:用户描述想画的图 → AI 生成符合 Excalidraw 规范的 JSON 元素 → 前端实时解析并渲染。

我这里前端使用的是 Next.js ,集成了官方的 @excalidraw/excalidraw 包。

后端使用 Vercel AI SDK 对接大模型,它支持 OpenAI 及兼容服务像智谱、阿里百炼等等。

核心实现

一、Excalidraw JSON 规范

Excalidraw 的绘图原理是通过 JSON 描述元素。每个元素有 idtypexywidthheight 这些基础属性,不同类型还有特定属性。

支持的元素类型:

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

一个简单的矩形元素示例:

json 复制代码
{
  "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"
    }
  ]
}

backgroundColor 是矩形的颜色,strokeColor 是边框颜色:

二、AI 提示词设计

为了让 AI 生成正确的 JSON 同时保证出图的效果,我们需要给 AI 提供一些必要的信息,比如:

  • 输出格式:纯 JSON,不要代码块(方便流式解析)
  • 文字处理:中英文宽度计算规则,形状内文字的双向绑定
  • 箭头规范:points 是相对坐标,各方向箭头的写法
  • 常用颜色:Excalidraw 内置调色板

我的 prompt 结构大概是这样的,仅供参考:

plain 复制代码
你是一个专业的 Excalidraw 绘图助手。用户会描述他们想要绘制的图形、流程图、架构图等,你需要生成对应的 Excalidraw 元素 JSON。
## 输出格式要求
1. **先说明,后输出**:先简要说明要画什么,然后连续输出所有 JSON 元素
2. **禁止**在 JSON 元素之间穿插说明文字
3. 每个元素直接输出纯 JSON 对象,以 { 开头,以 } 结尾
4. **禁止**使用代码块(禁止 \`\`\` 符号)
5. **必须**使用标准 JSON 格式:键值对用冒号分隔(如 "x":100),不要写成等号
## 文字处理
## 箭头和线条
## 元素基础结构
## 基础形状
## 常用颜色

因为实在是太长了,为了不影响文章的篇幅我就省略了一些内容,大家需要完整内容的话可以联系我。

三、流式 JSON 解析

AI 返回的内容是流式的,需要边接收边解析。核心解析逻辑:

typescript 复制代码
// 从流式文本中提取完整的 JSON 对象
function extractJsonObjects(text: string) {
  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++) {
      // ... 处理转义字符和字符串
      if (char === '{') depth++
      else if (char === '}') {
        depth--
        if (depth === 0) {
          // 找到完整的 JSON 对象
          results.push(text.slice(startIndex, j + 1))
          break
        }
      }
    }
  }
  return results
}

这里有三个关键点:

1)追踪花括号深度来处理嵌套 JSON

2)正确处理字符串内的花括号(避免误判)

3)记录已处理位置,避免重复解析

当然,可能还有一些我没想到的情况,遇到的话按需处理即可。

四、实时渲染

解析出元素后,通过 Excalidraw API 添加到画布:

typescript 复制代码
// 暴露给父组件的方法
useImperativeHandle(ref, () => ({
  addElements: (newElements) => {
    const api = excalidrawAPIRef.current
    const currentElements = api.getSceneElements()
    
    // 处理 id 冲突
    const elementsToAdd = newElements.map(el => {
      if (existingIds.has(el.id)) {
        return { ...el, id: generateNewId() }
      }
      return el
    })
    
    // 更新画布
    api.updateScene({
      elements: [...currentElements, ...elementsToAdd],
    })
  },
}))

到这一步核心流程就完成了,不过为了稳定性考虑,还可以进行下面的第五步处理。

五、元素默认值补全

AI 可能只生成必要字段,需要补全 Excalidraw 需要的其他字段,也算是一种降级策略:

typescript 复制代码
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,
  }
}

移动端适配

为了在手机上也能使用这个工具,我还做了移动端的适配,核心需求是 能用AI生成并看到效果,编辑之类的操作在移动端就不奢求了。移动端采用上下布局,即上面的画布 + 底部输入框:

数据持久化

这个工具所有的画布数据都保存在 localStorage 里,不会上传到服务器,所以大家可以放心使用。

🎯结语

以上就是 AI + Excalidraw 实现手绘风格绘图工具的完整实现啦,核心就是让 AI 生成符合规范的 JSON,然后流式解析并实时渲染。

至于效果的话,我只能说"流程是通的,效果是拉胯的",哈哈哈,所以目前这个阶段只适合作为玩具玩一玩,远远不够生产的水平,大家感兴趣的话可以到 www.lzkz.top/tool/excali... 直接体验。

不过因为这个工具写在了我的个人工具里,所以暂时没有开源,大家需要的话可以在评论区留言或者直接联系我!

如果有用的话,欢迎关注点赞一起交流进步!下期再见,拜拜👋🏻。

相关推荐
用户5191495848452 小时前
7-ZiProwler:CVE-2025-11001 漏洞利用工具
人工智能·aigc
湘-枫叶情缘2 小时前
管理认知平权:基于人工操作大语言模型的MBA“具生化”下沉路径
人工智能·语言模型
多则惑少则明2 小时前
AI大模型综合(三)Langgraph4j工作原理,RAG概念
人工智能·langchain4j
智能砖头2 小时前
LangGraph 工作流中常用的核心策略
人工智能
用户81274828151202 小时前
安卓Settings值原理源码剖析存储最大的字符数量是多少?
前端
玄同7652 小时前
Python 函数:LLM 通用逻辑的封装与复用
开发语言·人工智能·python·深度学习·语言模型·自然语言处理
用户81274828151202 小时前
安卓14剖析SystemUI的ShadeLogger/LogBuffer日志动态控制输出dumpsy机制
前端
踏浪无痕2 小时前
四个指标,一种哲学:Prometheus 如何用简单模型看透复杂系统
后端·架构·go
Ankkaya2 小时前
cloudflare + github 实现留言板
前端·github