Excalidraw 数据增量存储的构想

这几天,我给 Shiro 的 Markdown 支持加上了 Excalidraw 的画板。

innei.in/posts/progr...

上面的文章中,内嵌了大量用 Excalidraw 作的图。在 Markdown 中内嵌这种画板其实只要在渲染时候根据数据决定渲染组件就行了。Excalidraw 的数据格式是一个 JSON 数据,我原本的构想是直接利用 Markdown 的代码块格式,language 指定为 excalidraw,然后只要在渲染时候去判断就行了。

这样的做法是非常简单的。例如在 Markdown 中插入如下的内容。

markdown 复制代码
```excalidraw
{"type":"excalidraw","version":2,"source":"http://localhost:2323","elements":[{"id":"XoWT6cmhHOi8z5zAAX4jf","type":"rectangle","x":-222.2812271118164,"y":-161.63150024414062,"width":355.21875,"height":239.31640625,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":3},"seed":1343987013,"version":38,"versionNonce":982834725,"isDeleted":false,"boundElements":null,"updated":1706977127668,"link":null,"locked":false}],"appState":{"gridSize":null,"viewBackgroundColor":"#ffffff"}}
```

就会渲染这样的结果。

这很好。但是随着图的复杂,数据量非常庞大,内嵌在 Markdown 中属实不妥,而且更重要的是,当文档的字符数超过一定的范围之后,algoliaSearch 会被拒绝收录,比如下面作图。

经过压缩的 JSON 数据都来到了 32K。

如果一个文档中多包涵几个这样作图,整个文档的大小都快到好几百 K 了。而且内嵌的数据也是难以阅读。

我提出一个新的存储方案。

在 Markdown 中除了解析原有的数据结构之外以保证兼容性,另外引入引用的数据存储方式。例如下面的格式。

markdown 复制代码
```excalidraw
https://gist.githubusercontent.com/Innei/0e60d7ba7672147cc7b7c24480400428/raw/547522201e71189eed08bbd763eea65224872ce9/test.excalidraw
{"source":["https://excalidraw.com","http://localhost:2323"],"elements":{"0":{"boundElements":[null,[]]},"1":[{"type":"ellipse","version":11,"versionNonce":479831339,"isDeleted":false,"id":"euMxBYc3ouFIFs49-MrKV","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":290.70906829833984,"y":302.01168060302734,"strokeColor":"#1e1e1e","backgroundColor":"#ffec99","width":187,"height":235.40234375,"seed":1548041963,"groupIds":[],"frameId":null,"roundness":{"type":2},"boundElements":[],"updated":1706959069124,"link":null,"locked":false}],"2":[{"type":"rectangle","version":15,"versionNonce":1344649355,"isDeleted":false,"id":"qgVmshWC3FRwZzp1ROv4K","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":563.6192245483398,"y":286.01168060302734,"strokeColor":"#1e1e1e","backgroundColor":"#ffec99","width":346.0078125,"height":315.234375,"seed":1478608843,"groupIds":[],"frameId":null,"roundness":{"type":3},"boundElements":[],"updated":1706959070487,"link":null,"locked":false}],"3":[{"type":"arrow","version":10,"versionNonce":806397620,"isDeleted":false,"id":"_gL2MH5dBkS4qa5i7ZOk6","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":338.1563186645508,"y":262.195556640625,"strokeColor":"#1e1e1e","backgroundColor":"transparent","width":430.1640625,"height":35.09375,"seed":59309068,"groupIds":[],"frameId":null,"roundness":{"type":2},"boundElements":[],"updated":1706959508640,"link":null,"locked":false,"startBinding":null,"endBinding":null,"lastCommittedPoint":null,"startArrowhead":null,"endArrowhead":"arrow","points":[[0,0],[430.1640625,35.09375]]}],"_t":"a"},"files":[{},0,0]}
```

第一行为引用地址,这个存储的是最原始的作图数据,第二行位压缩的 JSON 数据,记录在原始数据集上增量的 Diff,在解析时,首先获取原始数据,然后用 patch 的方式获取修改之后的数据集。

当然如果没有增量数据时,就变成下面的形式:

markdown 复制代码
```excalidraw
https://gist.githubusercontent.com/Innei/0e60d7ba7672147cc7b7c24480400428/raw/547522201e71189eed08bbd763eea65224872ce9/test.excalidraw
```

这样的增量存储的方式,还可以复用原先的作图,在此基础上做增量修改。比如我做了下面的编辑功能。

在第一个作图的基础上增量绘制的新作图。

最后生成的 Markdown 内容是:

markdown 复制代码
```excalidraw
ref:file/koi3m8unhivw47rwan.excalidraw
```

```excalidraw
ref:file/koi3m8unhivw47rwan.excalidraw
{"elements":{"0":{"boundElements":[null,[]]},"1":[{"id":"mBu018nlCDYkLyN6lCxwn","type":"ellipse","x":-352.3143539428711,"y":-186.44375610351562,"width":283.37890625,"height":264.01171875,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"roundness":{"type":2},"seed":851926698,"version":13,"versionNonce":1222766506,"isDeleted":false,"boundElements":null,"updated":1706980523583,"link":null,"locked":false}],"_t":"a"}}
```

上面第一行的 ref: 是我做的 alias 和地址无区别

完整的代码实现可供参考:

github.com/Innei/sprig...

github.com/Innei/sprig...

原文出处:innei.in/posts/progr...

相关推荐
程序员小寒1 小时前
JavaScript设计模式(八):命令模式实现与应用
前端·javascript·设计模式·ecmascript·命令模式
wgod1 小时前
new AbortController()
前端
UXbot1 小时前
UXbot 是什么?一句指令生成完整应用的 AI 工具
前端·ai·交互·个人开发·ai编程·原型模式·ux
棒棒的唐2 小时前
WSL2用npm安装的openclaw,无法正常使用openclaw gateway start启动服务的问题
前端·npm·gateway
哔哩哔哩技术2 小时前
使用Compose Navigation3进行屏幕适配
前端
咬人喵喵3 小时前
E2.COOL 平台深度解析:从特效分类到实战操作指南
前端·编辑器·svg
RisunJan4 小时前
Linux命令-named-checkzone
linux·前端
小陈工4 小时前
Python Web开发入门(十):数据库迁移与版本管理——让数据库变更可控可回滚
前端·数据库·人工智能·python·sql·云原生·架构
吹晚风吧4 小时前
解决vite打包,base配置前缀,nginx的dist包找不到资源
服务器·前端·nginx
weixin199701080164 小时前
《施耐德商品详情页前端性能优化实战》
前端·性能优化