使用excalidraw搭建自己的中文手写画板
成品预览地址:https://guizimo.github.io/excalidraw/
原excalidraw
提供了英文的手写体,但中文还是正正方方的,感觉不搭。希望中文也可以有那样一种手写风格。
本文使用的是
excalidraw
,它是一个十分优秀的开源项目,地址:https://github.com/excalidraw/excalidraw
1、创建项目
1.1、拉取代码
首先在excalidraw
的Github
主页Fork
一份代码到自己的账户下
Fork
之后,clone
到本地
git clone git@github.com:guizimo/excalidraw.git
然后在这里,签出一个新的分支:zh-dev
,这个分支用来提交我们自定义的提交和变动,在后续如果excalidraw
更新后,可以合并主干,得到最新的特性。
1.2、运行
尝试本地运行
// 安装依赖
yarn install
// 运行
yarn run start
在浏览器中打开http://localhost:3000/,发现已经可以正常运行了
2、下载中文字体
选择一款你看的舒服的中文手写字体,当然这里你喜欢其他的字体也可。需要注意的是字体的版权问题哈。笔者在选择中文字体的时候发现了一个网站:猫啃网,大家有空可以自己去了解哈。
我这边选择的是平方雨桐体,在这里感谢作者提供那么好的字体!
下载好中文字体包,并重命名为 Yutong.ttf
。
3、加入中文字体
到这一步了需要手动调整代码了,因为不同的版本,代码会有一些调整。目前的
excalidraw
使用了monorepo
来管理项目了。这里提供一种思路,观察其中的一个内置的字体,通过断点调试了解它的加载流程,按照它的思路,添加我们自定义的字体即可。
3.1、放置字体资源
将我们之前下载好的字体文件 Yutong.ttf
放置到 packages/excalidraw/fonts/assets
和public
目录下。
3.2、注册字体
编辑 packages/excalidraw/fonts/assets/fonts.css
,添加中文手写体Yutong
:
@font-face {
font-family: "Yutong";
src: url(./Yutong.ttf) format("truetype");
style: normal;
display: swap;
}
编辑packages/excalidraw/index-node.ts
添加registerFont
。
registerFont("./public/Virgil.woff2", { family: "Virgil" });
registerFont("./public/Yutong.ff2", { family: "Yutong" });
registerFont("./public/Cascadia.woff2", { family: "Cascadia" });
3.3、预加载字体资源
编辑excalidraw-app/index.html
,添加一个link
。
<link
rel="preload"
href="../packages/excalidraw/fonts/assets/Yutong.ttf"
as="font"
type="font/ttf"
crossorigin="anonymous"
/>
编辑scripts/woff2/woff2-vite-plugins.js
,添加一个link
。
<link
rel="preload"
href="/Yutong.ttf"
as="font"
type="font/ttf"
crossorigin="anonymous"
/>
编辑 packages/excalidraw/constants.ts
,在 FONT_FAMILY
常量中加入字体的枚举。
这里可以把枚举的数字设置大一点,防止后续更新新加了字体,导致冲突。
export const FONT_FAMILY = {
Virgil: 1,
Helvetica: 2,
Cascadia: 3,
// leave 4 unused as it was historically used for Assistant (which we don't use anymore) or custom font (Obsidian)
Excalifont: 5,
Nunito: 6,
"Lilita One": 7,
"Comic Shanns": 8,
"Liberation Sans": 9,
Yutong: 999
}
编辑packages/excalidraw/components/FontPicker/FontPicker.tsx
,在列表中添加切换按钮。
这里懒得去添加图标和枚举了,我把
Nunito
的位置给占了哈,不像我这边懒的人可以去加上图标哈。
{
value: FONT_FAMILY.Yutong,
icon: FontFamilyNormalIcon,
text: t("labels.normal"),
testId: "font-family-yutong",
},
// {
// value: FONT_FAMILY.Nunito,
// icon: FontFamilyNormalIcon,
// text: t("labels.normal"),
// testId: "font-family-normal",
// },
编辑packages/excalidraw/fonts/index.ts
添加_register
。
import Yutong from "./assets/Yutong.ttf";
......
_register("Yutong", FONT_METADATA[FONT_FAMILY.Excalifont], {
uri: Yutong,
});
再次运行起来,那么在使用字体的第二项的时候,已经可以正常使用我们刚添加的中文手写字体了。
4、部署
在本地已经可以正常使用了,为了更加方便使用,这边直接选择使用Github Page
部署。可以借助gh-pages
来实现。
gh-pages的原理:
就是创建一个
gh-pages
的分支,将选中的已经构建好的资源文件,放入该分支,然后推送到远程的Github
仓库,Github
识别到gh-pages的分支有更新,会生成一个deploy
,通过该链接就可以正常访问了。
4.1、安装依赖
yarn add -D gh-pages
修改package.json
文件,添加deploy
脚本。
"scripts": {
......,
"deploy": "gh-pages -d excalidraw-app/build"
}
4.2、调整配置
注意:目前笔者是使用二级部署的,即部署之后访问的链接形式是:
https://xxx.com/xxx/excalidraw
,需要调整打包之后的资源引用路径。
编辑excalidraw-app/vite.config.mts
,使用相对路径base: './'
。
export default defineConfig({
......,
base: './', // 使用相对路径
......,
})
4.3、构建部署
首先打包成为静态资源
yarn run build
部署
yarn run deploy
执行之后,在远程仓库中,会自动创建github-pages deployments
。
这里会给到一个链接:https://guizimo.github.io/excalidraw/
打开链接就可以看到成功部署的中文手写版excalidraw
了。
编辑Github
主页配置。
4.4、踩坑
部署成功之后,自己添加的字体无法正常显示。
报错信息
DOMException: The source provided ('url(https://guizimo.github.io/excalidraw/assets/Yutong-BrR4G41P.ttf) format('ttf')') could not be parsed as a value list.
发现format('ttf')
并不是一个合法的格式。但是搜索整个代码,并未声明format('ttf')
这样的一种格式。
通过断点调试找到了在packages/excalidraw/fonts/ExcalidrawFont.ts
的getFormat
方法中,会将字体文件的后缀名作为format
,导致浏览器加载不上我们添加的ttf
格式字体。
改造getFormat
ttf
对应的格式为:truetype
。
private static getFormat(url: URL) {
try {
const pathname = new URL(url).pathname;
const parts = pathname.split(".");
if (parts.length === 1) {
return "";
}
// ttf is not a valid format, so we are converting it to truetype
let type = parts.pop()
if (type === 'ttf') {
type = 'truetype'
}
return `format('${type}')`;
} catch (error) {
return "";
}
}
各种字体对应的格式
- format(‘truetype’):用于指定 TrueType 字体(.ttf 文件)的格式。
- format(‘woff’):用于指定 Web Open Font Format 字体(.woff 文件)的格式。
- format(‘woff2’):用于指定 Web Open Font Format 2 字体(.woff2 文件)的格式。
- format(‘opentype’):用于指定 OpenType 字体(.otf 文件)的格式。
重新构建部署,自定义的字体已经生效!