一转眼就到 2024 年了!大家新年快乐!
前段时间在写文章时,需要一些配图,于是就使用了 TikZ 来绘制。TikZ 是一个强大的
如果你的阅读器看不到上面的 SVG 格式图片,可以点这里查看 PNG 格式。
上面的图对应的 TikZ 代码可以在这里找到。然而画是画爽了,想把它贴到博客里时却犯了难——目前竟然没有什么好办法可以直接在博客里使用 TikZ!
TL;DR
咱们废话不多说,直接上结果:我写了一个 Hexo 插件,可以直接把 Markdown 源码里的 TikZ 代码渲染成 SVG 矢量图,然后在博客构建时嵌入到页面 HTML 中,用起来就和 MathJax 写数学公式一样简单。
而且最重要的是渲染完全在构建时完成,浏览器上无需运行任何 JavaScript。同时构建机上也无需安装
👉 prinsss/hexo-filter-tikzjax: Server side PGF/TikZ renderer plugin for Hexo.
npm install hexo-filter-tikzjax
注意:插件安装成功后,需要运行 hexo clean
清除已有的缓存。
安装插件后,只需要在博客文章中添加 TikZ 代码块:
---
title: '使用 TikZ 在 Hexo 博客中愉快地画图'
tikzjax: true
---
Markdown text here...
```tikz
\begin{document}
\begin{tikzpicture}
% Your TikZ code here...
% The graph below is from https://tikz.dev/library-3d
\end{tikzpicture}
\end{document}
```
插件就会自动把代码渲染成对应的图片,非常方便:
TikZ 教程
不做过多介绍。贴几个链接,有兴趣的可以学学:
- [LaTeX 绘图指南 - 001] TikZ 的简介、资源以及学习方法 - 知乎
- Hansimov/pgfmanual-zh: PGF/TikZ 中文手册
- PGF/TikZ Manual - Complete Online Documentation
- xiaohanyu/awesome-tikz: A curated list of awesome TikZ documentations, libraries and resources
比如这就是我在写文章时画的图,全部用 TikZ 代码生成,画起来改起来都很方便。
原理
在本插件之前,主流的在网页上渲染 TikZ 绘图的方式是使用 TikZJax。TikZJax 有点类似 MathJax,都是通过 JavaScript 去渲染
<link rel="stylesheet" type="text/css" href="https://tikzjax.com/v1/fonts.css">
<script src="https://tikzjax.com/v1/tikzjax.js"></script>
<script type="text/tikz">
\begin{tikzpicture}
\draw (0,0) circle (1in);
\end{tikzpicture}
</script>
然而这样做的问题是,太重了。在网页上动态加载 TikZJax,需要加载 955KB 的 JavaScript + 454KB 的 WebAssembly + 1.1MB 的内存数据,如果再另外安装一些宏包,最终打包产物大小甚至可以达到 5MB+。
对于一些有加载性能要求的网站,这显然是难以接受的。
那怎么办呢?答案就是 SSR / SSG,把渲染过程搬到服务端/构建时去做。这很适合博客这样的场景,尤其是静态博客,只需要构建时渲染一下,把生成的图片塞到 HTML 里就完事了,完全不需要客户端 JavaScript 参与,加载速度大幅提升。
因为 TikZJax 底层跑的是 WebAssembly,而 Node.js 也支持运行 WebAssembly,所以很自然地我就想,能不能把它的渲染流程直接搬到 Node.js 里面去做?
说干就干。于是就有了 node-tikzjax,一个 TikZJax 的移植版,可以在纯 Node.js 环境下运行,无需安装第三方依赖或者
hexo-filter-tikzjax 则是 node-tikzjax 的一个上层封装,主要就是在渲染 Hexo 博客文章时提取 Markdown 源码中的 TikZ 代码,交给 node-tikzjax 执行并渲染出 SVG 图片,然后将其内联插入到最终的 HTML 文件中。
如果是其他博客框架,也可以用类似的原理实现 TikZ 静态渲染的接入。
局限性
因为 node-tikzjax 并不是完整的 \usepackage{}
中直接使用的宏包有:
- chemfig
- tikz-cd
- circuitikz
- pgfplots
- array
- amsmath
- amstext
- amsfonts
- amssymb
- tikz-3dplot
如果希望添加其他宏包,可以参考 extractTexFilesToMemory 这里的代码添加。
如果在使用插件的过程中 TikZ 代码编译失败了,可以通过 hexo s --debug
或者 hexo g --debug
开启调试模式,查看
This is e-TeX, Version 3.14159265-2.6 (preloaded format=latex 2022.5.1)
**entering extended mode
(input.tex
LaTeX2e <2020-02-02> patch level 2
("tikz-cd.sty" (tikzlibrarycd.code.tex (tikzlibrarymatrix.code.tex)
(tikzlibraryquotes.code.tex) (pgflibraryarrows.meta.code.tex)))
No file input.aux.
ABD: EveryShipout initializing macros [1] [2] (input.aux) )
Output written on input.dvi (2 pages, 25300 bytes).
Transcript written on input.log.
或者也可以在这个 Live Demo 中输入你的 TikZ 代码,提交后可在控制台查看报错。
致谢
首先要感谢 @kisonecat 开发的 web2js 项目,这是一个 Pascal 到 WebAssembly 的编译器,使我们可以在 JavaScript 中运行
这里有作者关于构建基于 Web 的
感谢 @drgrice1 对 TikZJax 和 dvi2html 的修改,TA 的 fork 中包含了很多有用的新功能,并且修复了一些原始代码中的问题。
感谢 @artisticat1 对 TikZJax 的修改,这是基于上述 @drgrice1 的 fork 的又一个 fork,也添加了一些有用的功能。本插件依赖的 node-tikzjax,其底层使用的 WebAssembly 二进制和其他文件就是从这个仓库中获取的。
感谢 @artisticat1 开发的 obsidian-tikzjax 插件,这是本项目的灵感来源。本项目和该插件底层共享同一套
如有任何问题,请在 GitHub 上提交 issue。祝使用愉快!