Normal view

There are new articles available, click to refresh the page.
Before yesterdayPRIN BLOG

使用 TikZ 在 Hexo 博客中愉快地画图

By: prin
16 January 2024 at 04:00

一转眼就到 2024 年了!大家新年快乐!

前段时间在写文章时,需要一些配图,于是就使用了 TikZ 来绘制。TikZ 是一个强大的 宏包,可以使用代码的形式绘制出各种各样精美的矢量图。

如果你的阅读器看不到上面的 SVG 格式图片,可以点这里查看 PNG 格式。example-tikz-graph

上面的图对应的 TikZ 代码可以在这里找到。然而画是画爽了,想把它贴到博客里时却犯了难——目前竟然没有什么好办法可以直接在博客里使用 TikZ!

TL;DR

咱们废话不多说,直接上结果:我写了一个 Hexo 插件,可以直接把 Markdown 源码里的 TikZ 代码渲染成 SVG 矢量图,然后在博客构建时嵌入到页面 HTML 中,用起来就和 MathJax 写数学公式一样简单。

而且最重要的是渲染完全在构建时完成,浏览器上无需运行任何 JavaScript。同时构建机上也无需安装 环境,因为其底层运行的是 WebAssembly。

👉 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 教程

不做过多介绍。贴几个链接,有兴趣的可以学学:

比如这就是我在写文章时画的图,全部用 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 环境下运行,无需安装第三方依赖或者 环境。轻量化的特性很适合拿来做服务端渲染,也支持在 Cloudflare Worker 等 Runtime 上运行,非常好用。

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.texLaTeX2e <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 的 引擎的一篇文章,可以拜读一下:Both TEX and DVI viewers inside the web browser

感谢 @drgrice1 对 TikZJax 和 dvi2html 的修改,TA 的 fork 中包含了很多有用的新功能,并且修复了一些原始代码中的问题。

感谢 @artisticat1 对 TikZJax 的修改,这是基于上述 @drgrice1 的 fork 的又一个 fork,也添加了一些有用的功能。本插件依赖的 node-tikzjax,其底层使用的 WebAssembly 二进制和其他文件就是从这个仓库中获取的。

感谢 @artisticat1 开发的 obsidian-tikzjax 插件,这是本项目的灵感来源。本项目和该插件底层共享同一套 引擎,使用语法也很类似,基本可以在 Obsidian 和 Hexo 之间无缝切换(实际上也是我开发这个的原因 😹)。

如有任何问题,请在 GitHub 上提交 issue。祝使用愉快!

使用 GitHub Actions 自动部署 Hexo 博客

By: prin
7 February 2021 at 04:05

联动三年前的文章:使用 Travis CI 自动部署 Hexo 博客

今天更新了一下博客,寻思着好歹也改一下页脚的 Copyright 年份,改完 push 上去以后却发现 GitHub Pages 迟迟没有更新。进去 Travis CI 一看,发现任务一直处于 Queued 状态,半小时了都没开始构建。

查了一下,并不是只有我遇到了类似情况(似乎是因为 Travis CI 正在将 travis-ci.org 迁移至 travis-ci.com):

看了一圈感觉有点悬,干脆换成 GitHub Actions 吧。

我的博客完全托管在 GitHub 上:prinsss.github.io,其中 source 分支放的是源码,master 分支(即 GitHub Pages)是 Hexo 生成的静态博客页面。

要做的也和之前 Travis CI 差不多,当 source 分支有更新时,自动使用 Hexo 构建新页面并更新 GitHub Pages 就可以了。

配置部署密钥

生成一个新的 SSH 密钥,用于 push 至 GitHub Pages 所在的 repo:

ssh-keygen -f hexo-deploy-key -C "prinsss.github.io"

将公钥 hexo-deploy-key.pub 设置为仓库的部署密钥(Settings > Deploy keys):

add-deploy-key

然后在 Settings > Secrets 中新增一个 secret,命名为 DEPLOY_KEY,把私钥 hexo-deploy-key 的内容复制进去,供后续使用。

编写 Workflow

Workflow 就是 GitHub Actions 的配置文件,类似于 .travis.yml

首先新建文件:

mkdir -p .github/workflowstouch .github/workflows/deploy.yml

编辑 deploy.yml

name: Hexo Deploy# 只监听 source 分支的改动on:  push:    branches:      - source# 自定义环境变量env:  POST_ASSET_IMAGE_CDN: truejobs:  build-and-deploy:    runs-on: ubuntu-latest    steps:      # 获取博客源码和主题      - name: Checkout        uses: actions/checkout@v2      - name: Checkout theme repo        uses: actions/checkout@v2        with:          repository: prinsss/hexo-theme-murasaki          ref: master          path: themes/murasaki      # 这里用的是 Node.js 14.x      - name: Set up Node.js        uses: actions/setup-node@v1        with:          node-version: '14'      # 设置 yarn 缓存,npm 的话可以看 actions/cache@v2 的文档示例      - name: Get yarn cache directory path        id: yarn-cache-dir-path        run: echo "::set-output name=dir::$(yarn cache dir)"      - name: Use yarn cache        uses: actions/cache@v2        id: yarn-cache        with:          path: ${{ steps.yarn-cache-dir-path.outputs.dir }}          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}          restore-keys: |            ${{ runner.os }}-yarn-      # 安装依赖      - name: Install dependencies        run: |          yarn install --prefer-offline --frozen-lockfile      # 从之前设置的 secret 获取部署私钥      - name: Set up environment        env:          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}        run: |          sudo timedatectl set-timezone "Asia/Shanghai"          mkdir -p ~/.ssh          echo "$DEPLOY_KEY" > ~/.ssh/id_rsa          chmod 600 ~/.ssh/id_rsa          ssh-keyscan github.com >> ~/.ssh/known_hosts      # 生成并部署      - name: Deploy        run: |          npx hexo deploy --generate

当然,具体步骤还是得根据自己的需求进行相应的修改。

GitHub Pages 相关的具体配置放在了 Hexo 的 _config.yml 里:

deploy:  type: git  repo: git@github.com:prinsss/prinsss.github.io.git  branch: master  name: prinsss  email: prinsss@gmail.com

部署结果

更新 source 分支,push 后 GitHub Actions 就会自动执行。

deploy-result

不到半分钟就 build 完了,只能说微软爸爸还是牛逼。

参考链接

❌
❌