跳过正文
  1. 文章/

Excalidraw:手绘风格的在线白板工具

sun.ao
作者
sun.ao
我是 sun.ao,一名热爱技术的程序员,专注于 AI 和数智化领域。
目录

前言
#

在远程办公和在线协作日益普及的今天,白板工具成为了团队沟通的重要载体。Excalidraw 以其独特的手绘风格和极简设计,在众多白板工具中脱颖而出。它不仅是一款优秀的绘图工具,更是一个开源项目的技术典范。

什么是 Excalidraw?
#

Excalidraw 是一款开源的虚拟白板工具,专注于创建具有手绘风格的图表、草图和示意图。它的核心特点包括:

特性描述
手绘风格独特的"抖动"效果,让图形看起来像手绘的
极简界面无干扰的绘图体验,学习成本几乎为零
实时协作支持多人同时编辑,适合远程团队
离线支持PWA 应用,可离线使用
开源免费MIT 协议,可自由使用和二次开发

核心功能介绍
#

1. 绘图工具
#

Excalidraw 提供了丰富的绘图工具,覆盖大部分绘图需求:

  • 矩形、菱形、椭圆:基础形状,支持圆角调整
  • 箭头与线条:支持曲线、直线,可添加箭头
  • 文本工具:支持富文本,可调整字体大小
  • 自由绘制:手绘模式,自由创作
  • 图片导入:支持拖拽导入图片

2. 手绘风格算法
#

Excalidraw 最具特色的是其手绘风格算法。核心思路是为线条添加"抖动"效果:

// 简化的手绘效果实现
function generateRoughLine(points, options) {
  const { roughness, bowing } = options;

  return points.map((point, i) => {
    // 添加随机偏移
    const offsetX = (Math.random() - 0.5) * roughness;
    const offsetY = (Math.random() - 0.5) * roughness;

    // 添加弯曲效果
    const bowOffset = Math.sin(i * 0.5) * bowing;

    return {
      x: point.x + offsetX + bowOffset,
      y: point.y + offsetY
    };
  });
}

实际上,Excalidraw 使用了 Rough.js 库来实现手绘效果,该库提供了丰富的手绘风格选项:

import rough from 'roughjs';

const rc = rough.canvas(canvasElement);

// 绘制手绘风格的矩形
rc.rectangle(10, 10, 100, 50, {
  stroke: '#000000',
  strokeWidth: 2,
  roughness: 1.5,    // 粗糙度
  bowing: 1,         // 弯曲度
  fill: '#ffffff',
  fillStyle: 'solid'
});

3. 实时协作
#

Excalidraw 支持多人实时协作,技术实现基于:

  • WebSocket:实时同步绘制操作
  • CRDT(Conflict-free Replicated Data Types):解决并发编辑冲突
  • 广播机制:每个操作广播给所有协作者
// 简化的协作同步逻辑
class CollaborationManager {
  constructor(socket) {
    this.socket = socket;
    this.localVersion = 0;
  }

  // 发送本地操作
  broadcastAction(action) {
    this.socket.emit('draw', {
      action,
      version: ++this.localVersion,
      timestamp: Date.now()
    });
  }

  // 接收远程操作
  onRemoteAction(data) {
    if (data.version > this.localVersion) {
      this.applyAction(data.action);
      this.localVersion = data.version;
    }
  }
}

4. 无限画布
#

Excalidraw 采用无限画布设计,支持:

  • 缩放:鼠标滚轮或手势缩放
  • 平移:拖拽移动画布
  • 坐标系:基于场景坐标,不受画布尺寸限制
// 视口变换
class Viewport {
  constructor() {
    this.zoom = 1;
    this.scrollX = 0;
    this.scrollY = 0;
  }

  // 场景坐标转屏幕坐标
  sceneToScreen(x, y) {
    return {
      x: (x - this.scrollX) * this.zoom,
      y: (y - this.scrollY) * this.zoom
    };
  }

  // 屏幕坐标转场景坐标
  screenToScene(x, y) {
    return {
      x: x / this.zoom + this.scrollX,
      y: y / this.zoom + this.scrollY
    };
  }
}

技术架构
#

技术栈
#

技术用途
ReactUI 框架
TypeScript类型安全
Rough.js手绘效果渲染
Socket.io实时通信
YjsCRDT 实现
Vite构建工具

项目结构
#

excalidraw/
├── src/
│   ├── components/       # React 组件
│   │   ├── App.tsx
│   │   ├── Canvas.tsx
│   │   └── ...
│   ├── element/          # 图形元素定义
│   │   ├── newElement.ts
│   │   ├── mutateElement.ts
│   │   └── ...
│   ├── scene/            # 场景管理
│   ├── renderer/         # 渲染逻辑
│   ├── data/             # 数据持久化
│   └── collab/           # 协作功能
├── public/
│   └── index.html
└── package.json

核心数据结构
#

Excalidraw 的核心是元素(Element)数据结构:

type ExcalidrawElement = {
  id: string;
  type: "rectangle" | "ellipse" | "diamond" | "arrow" | "line" | "text" | "freedraw";
  x: number;
  y: number;
  width: number;
  height: number;
  angle: number;
  strokeColor: string;
  backgroundColor: string;
  fillStyle: "solid" | "hachure" | "cross-hatch";
  strokeWidth: number;
  roughness: number;
  opacity: number;
  // ... 更多属性
};

type AppState = {
  viewBackgroundColor: string;
  currentItemStrokeColor: string;
  zoom: { value: number };
  scrollX: number;
  scrollY: number;
  // ... 更多状态
};

实际应用场景
#

1. 架构图绘制
#

Excalidraw 非常适合绘制系统架构图:

┌─────────────┐      ┌─────────────┐
│   Client    │─────▶│   Server    │
└─────────────┘      └──────┬──────┘
                     ┌─────────────┐
                     │  Database   │
                     └─────────────┘

2. 流程图与思维导图
#

手绘风格让流程图更加生动,适合:

  • 产品需求讨论
  • 技术方案设计
  • 会议记录

3. 快速原型
#

设计师可以用 Excalidraw 快速绘制 UI 原型:

  • 线框图
  • 交互流程
  • 页面布局

4. 教学与演示
#

教育工作者可以用它:

  • 绘制示意图
  • 解释复杂概念
  • 制作教学材料

集成与扩展
#

嵌入到自己的项目
#

Excalidraw 提供了 npm 包,可以嵌入到任何 React 项目中:

npm install @excalidraw/excalidraw
import { Excalidraw } from "@excalidraw/excalidraw";
import "@excalidraw/excalidraw/index.css";

function App() {
  return (
    <div style={{ height: "500px" }}>
      <Excalidraw
        initialData={{
          elements: [],
          appState: {}
        }}
        onChange={(elements, appState) => {
          console.log("Elements:", elements);
        }}
      />
    </div>
  );
}

导出功能
#

Excalidraw 支持多种导出格式:

import { exportToSvg, exportToBlob, exportToCanvas } from "@excalidraw/excalidraw";

// 导出为 SVG
const svg = await exportToSvg({
  elements,
  appState,
  files
});

// 导出为 PNG Blob
const blob = await exportToBlob({
  elements,
  appState,
  files,
  mimeType: "image/png"
});

// 下载文件
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "diagram.png";
a.click();

自定义工具
#

可以通过 API 扩展自定义工具:

// 自定义元素类型
const customElement = {
  type: "custom",
  x: 100,
  y: 100,
  width: 200,
  height: 100,
  // 自定义渲染逻辑
  customData: {
    // 你的自定义数据
  }
};

与其他工具对比
#

工具风格协作开源价格
Excalidraw手绘支持免费
Miro精致支持付费
Draw.io标准有限免费
Figma精致支持免费/付费
Whimsical手绘支持付费

最佳实践
#

1. 快捷键使用
#

熟练使用快捷键可以大幅提升效率:

快捷键功能
R矩形工具
O椭圆工具
D菱形工具
A箭头工具
T文本工具
H手绘工具
V选择工具
Space + 拖拽平移画布
Ctrl/Cmd + 滚轮缩放

2. 库(Library)的使用
#

Excalidraw 支持自定义图形库,可以保存常用图形:

// 创建自定义库
const libraryItems = [
  {
    status: "published",
    elements: [
      // 你的自定义图形元素
    ]
  }
];

// 导入库
await Library.loadLibrary(libraryItems);

3. 文件格式
#

Excalidraw 使用 JSON 格式存储数据:

{
  "type": "excalidraw",
  "version": 2,
  "source": "https://excalidraw.com",
  "elements": [
    {
      "type": "rectangle",
      "version": 1,
      "x": 100,
      "y": 100,
      "width": 200,
      "height": 100
    }
  ],
  "appState": {
    "viewBackgroundColor": "#ffffff"
  }
}

总结
#

Excalidraw 以其独特的手绘风格和极简设计,成为了白板工具领域的一股清流。它的成功不仅在于产品本身,更在于开源社区的活跃和技术的开放性。

核心优势
#

  1. 独特风格:手绘效果让图表更有温度
  2. 开箱即用:无需学习,上手即用
  3. 开源免费:MIT 协议,可自由使用
  4. 可扩展:提供 npm 包,易于集成

适用场景
#

  • 技术架构图绘制
  • 产品原型设计
  • 团队协作讨论
  • 教学演示

参考资源
#

如果你正在寻找一款简洁、优雅的白板工具,不妨试试 Excalidraw!

相关文章