当前位置: 首页 > news >正文

用Excalidraw打造中文手写风格画板

用 Excalidraw 打造中文手写风格画板

在技术团队的日常协作中,一张随手勾勒的草图往往比千言万语更高效。无论是架构讨论、流程推演,还是产品原型构思,可视化表达始终是沟通的核心工具。而 Excalidraw 正是这一场景下的明星选手——它以极简界面和“手绘风”视觉效果著称,让数字白板拥有了纸笔般的亲和力。

但当我们真正投入中文环境使用时,却总感觉哪里不对劲:英文文本用了 Virgil 这类手写字体,线条歪歪扭扭很自然;可一旦输入中文,立刻变成规整冷峻的黑体或宋体,像是一张温柔的手稿里突然插入了打印标签,风格割裂感扑面而来。

有没有办法让中文也“写”得随意一点?答案是肯定的。本文将带你从零开始,深度定制 Excalidraw,引入一款优雅的开源中文字体,实现真正的“全手写风”统一体验,并部署为可共享的 Web 应用。

整个过程不仅解决了一个具体问题,更是一次对现代前端项目架构的实战演练:如何理解 Monorepo 结构、干预字体加载机制、处理服务端渲染兼容性,以及完成静态站点发布。


准备工作:获取并运行 Excalidraw 源码

要定制一个开源项目,第一步永远是跑通它的本地开发环境。

前往 Excalidraw 官方仓库,点击右上角Fork按钮复制到你的 GitHub 账户下,然后克隆到本地:

git clone git@github.com:yourname/excalidraw.git cd excalidraw

建议创建一个独立分支用于本次功能开发:

git checkout -b feature/chinese-handwriting-font

这样后续即使官方更新主干代码,也能通过 rebase 或 merge 平滑合并,保留我们的修改痕迹。

接下来安装依赖并启动服务:

yarn install yarn run start

Excalidraw 使用 Yarn 管理依赖,基于 Vite 构建。几分钟后浏览器会自动打开 http://localhost:3000,你应该能看到熟悉的绘图界面,可以自由添加形状、文本、箭头等元素。

✅ 提示:若启动失败,请确认 Node.js 版本是否为 v16+,这是项目推荐的基础运行环境。


字体选择:寻找一款“会呼吸”的中文字体

我们想要的不是普通的书法字,而是一种带有轻微抖动、笔画粗细变化、仿佛真实书写留下的痕迹——这种质感才能与 Excalidraw 的“手绘风”融为一体。

关键要求如下:
- 支持常用汉字(至少覆盖 GBK)
- 视觉风格自然流畅,避免机械感
- 免费可商用,规避版权风险

目前市面上有不少优秀开源字体平台可供筛选:
-猫啃网(maoken.com):专注免费可商用字体分享,标注清晰
-字由(hellofont.cn):设计师常用资源库
-Google Fonts 中文镜像:部分字体支持

本文选用“平方雨桐体”(YuTong Type Foundry 出品),这是一款圆润自然、略带连笔感的开源字体,其轻重变化恰到好处,非常适合模拟手写情境下的非均匀运笔效果。

下载.ttf格式的文件后,重命名为Yutong.ttf,便于后续引用。


集成字体:打通客户端与服务端的渲染链路

Excalidraw 采用 Monorepo 架构,字体系统分布在多个子包中。我们需要同时配置前端显示和服务端导出能力,否则会出现“页面看着正常,导出图片变黑体”的尴尬情况。

1. 放置字体文件

Yutong.ttf复制到两个关键目录:

cp Yutong.ttf packages/excalidraw/fonts/assets/ cp Yutong.ttf public/

前者用于构建时打包进字体模块,后者供 Node.js 服务直接提供静态资源访问。

2. 声明字体:CSS @font-face

编辑packages/excalidraw/fonts/assets/fonts.css,末尾添加:

@font-face { font-family: "Yutong"; src: url(./Yutong.ttf) format("truetype"); style: normal; display: swap; }

⚠️ 注意:必须写format("truetype")而非format("ttf"),这是 WOFF 规范的要求,否则浏览器可能拒绝解析。

3. Node 端注册字体(导出 PNG/SVG 必须)

编辑packages/excalidraw/index-node.ts,找到已有字体注册代码:

registerFont("./public/Virgil.woff2", { family: "Virgil" }); registerFont("./public/Cascadia.woff2", { family: "Cascadia" });

在其下方加入:

registerFont("./public/Yutong.ttf", { family: "Yutong" });

这一步至关重要。没有它,当你点击“Export as PNG”时,Canvas 将无法绘制该字体,导致中文回退为默认字体甚至乱码。

4. 添加预加载提升性能

为了避免首次加载时出现字体闪烁(FOIT/FOUT),需在 HTML 中预加载字体资源。

修改excalidraw-app/index.html,在<head>内添加:

<link rel="preload" href="/Yutong.ttf" as="font" type="font/ttf" crossorigin="anonymous" />

如果你使用了自定义 Vite 插件处理字体注入(如scripts/woff2/woff2-vite-plugins.js),也应同步添加此 preload 标签。

5. 扩展字体枚举常量

打开packages/excalidraw/constants.ts,查找FONT_FAMILY对象:

export const FONT_FAMILY = { Virgil: 1, Helvetica: 2, Cascadia: 3, Excalifont: 5, Nunito: 6, "Lilita One": 7, };

为了避免与未来官方新增字体冲突,我们使用一个较大的数值作为键值:

Yutong: 999

最终结果为:

export const FONT_FAMILY = { // ... existing fonts Yutong: 999 };
6. 添加字体选择器 UI

编辑packages/excalidraw/components/FontPicker/FontPicker.tsx,这是顶部工具栏中的字体下拉菜单组件。

在选项数组中新增一项:

{ value: FONT_FAMILY.Yutong, icon: FontFamilyHandDrawnIcon, // 复用手绘图标 text: "手写体 (中文)", testId: "font-family-yutong", },

💡 可选优化:你可以将"手写体 (中文)"提取为国际化字段,写入lang/zh-CN.json等语言包,实现多语言支持。

7. 注册字体元数据(防止 fallback)

最后一步,确保字体被正确纳入 Excalidraw 内部的字体管理系统。

编辑packages/excalidraw/fonts/index.ts,在_register调用后添加:

import Yutong from "./assets/Yutong.ttf"; // ... _register("Yutong", FONT_METADATA[FONT_FAMILY.Excalifont], { uri: Yutong, });

这里复用了Excalifont的度量信息(如行高、基线偏移),虽然不完全匹配,但在大多数场景下足以保证排版一致性。如果追求极致精准,也可以单独测量并定义新的FONT_METADATA条目。


功能验证:看看“写的字”像不像人写的

重新启动应用:

yarn run start

进入页面后操作如下:
1. 点击左侧 “T” 文本工具
2. 输入一段中文,例如:“用户登录流程”
3. 在顶部样式面板中选择新添加的“手写体 (中文)”选项
4. 观察文字是否呈现自然笔触

✅ 成功标志:中文字体呈现出轻微的粗细波动和圆润转角,与英文手绘线条风格协调一致,整体画面不再有割裂感。

此时你已经拥有了一款真正意义上的“双语手绘风”画板。


构建与部署:让它被世界看见

为了让团队成员或公众也能使用这个定制版本,我们可以将其部署为静态站点。推荐使用GitHub Pages实现免费托管。

1. 安装 gh-pages 工具
yarn add -D gh-pages
2. 添加 deploy 脚本

package.jsonscripts字段中增加:

"deploy": "gh-pages -d excalidraw-app/build"

完整示例如下:

"scripts": { "start": "concurrently \"yarn:start:app\" \"yarn:start:svr\"", "build": "yarn build:app", "deploy": "gh-pages -d excalidraw-app/build" }
3. 配置相对路径(适配二级目录)

如果你计划部署在https://yourname.github.io/excalidraw-zh/这样的子路径下,必须启用相对路径。

编辑excalidraw-app/vite.config.mts

export default defineConfig({ base: './', // 改为相对路径 // ... });

否则所有资源请求都会指向根目录/assets/...,导致 404 错误。

4. 执行构建与部署

依次运行:

yarn run build yarn run deploy

gh-pages会自动创建一个名为gh-pages的分支,并推送构建产物。几分钟后,前往仓库 Settings > Pages 即可看到部署成功的提示。

访问链接形如:https://yourname.github.io/excalidraw-zh/

即可全球访问你专属的中文手写风格 Excalidraw 画板


常见问题与解决方案(实战踩坑记录)

❌ 问题1:字体未生效,显示为方块或默认字体

原因分析
-@font-faceformat('ttf')不合法
- 浏览器缓存旧 CSS 或字体文件
- 字体路径错误或未正确复制到public/

解决方法
1. 检查fonts.css是否使用format("truetype")
2. 清除浏览器缓存(Ctrl + F5 强刷)
3. 打开 DevTools Network 面板,确认Yutong.ttf是否成功加载

❌ 问题2:导出 PNG/SVG 时中文乱码或缺失

根本原因:Node.js 环境未注册字体。

修复方式:务必确认已在index-node.ts中调用registerFont(),且路径相对于public/目录正确无误。

❌ 问题3:部署后报错Could not parse as value list

典型错误信息:

DOMException: The source provided ('url(/assets/Yutong-Bla.ttf) format('ttf')') could not be parsed...

根源:Excalidraw 内部根据扩展名自动生成format(xxx),而.ttf被误判为ttf,但标准应为truetype

修复方案

修改packages/excalidraw/fonts/ExcalidrawFont.ts中的getFormat方法:

private static getFormat(url: URL) { try { const pathname = new URL(url).pathname; const parts = pathname.split("."); if (parts.length === 1) return ""; let ext = parts.pop()?.toLowerCase(); switch (ext) { case "ttf": return "format('truetype')"; case "woff": return "format('woff')"; case "woff2": return "format('woff2')"; case "otf": return "format('opentype')"; default: return ""; } } catch (error) { return ""; } }

重新构建后,该问题即可消除。


进阶方向:结合 AI 快速生成图表

Excalidraw 不只是一个绘图工具,它还能成为AI 驱动的知识生产引擎

想象一下这样的场景:你在会议中说了一句“帮我画个电商系统的微服务架构”,下一秒白板上就出现了带注释的组件图——这不是科幻,而是完全可以实现的工作流。

推荐集成路径:
方案说明
Excalidraw + Mermaid + AI Prompt利用 AI 解析自然语言生成 Mermaid 代码,再通过转换器导入 Excalidraw
Custom AI Plugin开发插件调用 OpenAI API,输入描述返回 JSON 元素数组
Obsidian 插件联动若你使用 Obsidian,可通过其 Excalidraw 插件实现笔记内嵌 AI 生图

示例 Prompt:

“请生成一个包含用户登录、订单管理、支付网关的电商系统架构图,使用手绘风格。”

这类能力不仅能极大提升敏捷开发中的原型效率,也让非技术人员能轻松参与系统设计。


这种高度集成的设计思路,正引领着智能协作工具向更自然、更高效的方向演进。而你刚刚完成的,不只是一个字体替换,更是一次对开源生态的创造性回应——当工具不再只是“可用”,而是真正“好用”于你的母语文化时,创造力才真正获得了自由。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

http://www.cnnetsun.cn/news/85441.html

相关文章:

  • 4.3POSIXskin的不兼容性
  • 40、Perl与操作系统:Windows环境下的应用与操作
  • 盘点!国内几款特色AI大模型
  • 44、Perl引用的使用与深入探究
  • GPT-SoVITS语音合成与音色克隆实战指南
  • GPT-OSS-20B与Qwen3-14B九维全面对比
  • Docker与本地配置PaddleOCR实战指南
  • 从入门到精通:Agent任务分解终极指南,一篇彻底讲透技术栈与实战!
  • AutoGPT入门指南:安装、使用与案例全解析
  • 27、文本编辑器的复杂性与设计权衡
  • 基于java + vue校园快递物流管理系统(源码+数据库+文档)
  • AI时代工作模式革命:揭秘’人+智能体+机器人’新范式,重构未来职业与教育方向!
  • TOB企业获客软件选型指南:技术架构、核心能力与可信赖度深度剖析
  • 突破Seed-Coder-8B上下文限制的三大策略
  • Gfast 快速开发框架 V3.3.10 版发布
  • 稀土网络指标(2018-2024)
  • vue基于Spring Boot框架自然灾害应急救援捐赠平台_jwwh8v3n
  • 基于springboot和vue的陶瓷销售商城平台的设计与实现_87274i2a(java毕业设计项目源码)
  • 基于springboot和vue的高校晒衣服交流系统 物品收纳空间管理系统_76216q80(java毕业设计项目源码)
  • 巴菲特的投资方法与成功要素
  • 零基础学朴素贝叶斯:从数学原理到Python实现
  • 电商网站解决MIME类型警告的完整方案
  • AI一键搞定SQL Server安装:快马智能配置助手
  • 零基础学会用WebUI构建第一个网页应用
  • 1小时搞定!用WinStep.NTE快速验证Windows应用创意
  • 闪电开发:用auto-py-to-exe快速验证商业创意原型
  • vue3父子组件通信实战应用案例分享
  • 黑白老照片AI一键上色修复,效果惊艳!可离线使用,支持批量处理,太好用啦~
  • 企业级网络管理:NetworkManager在云服务器中的高级应用
  • 中文文档处理最佳实践:Anything-LLM支持UTF-8编码上传与解析