现已支持在文章中显示公式(LaTeX + MathJax)

陪她去流浪 桃子 2018年05月13日 阅读次数:2560

最近学数学比较多,可能会贴一些公式,所以给博客增加了数学公式显示的功能,写篇文章记录一下过程。

2024-04-20 更新:因为简化数学公式的处理,本文的一些观点可能已经过时/页面不能正常显示。

公式的显示借助 MathJax.js 插件(好像没有其它选择,不过它已经非常完善了),它能在几乎所有浏览器上排版出非常漂亮的数学公式。

MathJax 简介

MathJax 用于在网页中显示公式,支持的公式输入包括:LaTeX、MathML、AsciiMath,公式输出包括:HTML-CSS、SVG、CommonHTML等。

注意:如果采用默认的语法,则只需要引入一个 JavaScript 文件即可。本文后面的内容就不用关心了。

使用方式评估

MathJax 默认是自动排版整个页面的公式,公式也不要求放在 HTML 标签内,这与 LaTex 的方式很像。公式直接混合在文字中,并用 $...$ 或 $$...$$ 或 (...) 括起来。 比如:这是一个 $y=x^2$ 公式(我不支持这种方式,不予显示)。

因为公式没有被放在单独的HTML标签(比如 code)中,这样可能有几个小问题:

  • 如果公式包含 < 小于符号,可能会被错误解析。否则可能要写成 &lt; 这种形式,比较麻烦
  • 由于是直接嵌入在文字中的,解析器可能会遍历整个DOM树的节点来查找公式,找到后进行文本替换,可能稍有性能问题(具体不知道它是怎么处理的)

同时,由于我的博客文章是用 Markdown 写的,公式的插入应该像插入代码片段一样简单,所以我决定把它们放在 `` 中。

比如:

  • 行内公式

    这是一个 `$y=x^2$` 公式。

  • 块公式

    这是一个 ```$$y=x^2$$``` 公式。

这样还有一个好处:避免与代码的插入方式冲突。

注:MathJax 默认不处理 <code></code> 中的数学公式。

这样一来,恰好避免了上面两个小问题。并且,由于 <code></code> 默认是有背景色的(你看到了没?),所以就算没有显示公式,公式也很好和文字区分开来。

具体实施方案

GitHub提交记录:新插件:MathJax,用于显示公式 · movsb/taoblog@25aa3d7

  • 像插入代码一样在 Markdown 中插入公式

  • Markdown 编译器把公式编译进 <code></code> 标签中

  • 初始化 MathJax 的配置

    这里值得一提的是,MathJax 要求我们先在一个 <script type="text/x-mathjax-config"></script> 的标签中写入相关的配置,然后才引入插件,目的是为了减少不必要的资源加载。 我的初始化代码如下:

    <script type="text/x-mathjax-config">
        MathJax.Hub.Config({
            jax: ["input/TeX", "output/CommonHTML"],
            extensions: ["tex2jax.js"],
            tex2jax: {
                skipTags: [],
                inlineMath: [['$', '$']],
                displayMath: [['$$', '$$']],
            },
            skipStartupTypeset: true,
            //showMathMenu: false,
            menuSettings: {
                zoom: 'Click',
            }
        });
    </script>
    

    从配置可以看出,我只启用了对 LaTeX 的支持,并禁止了自动排版,并设置了单击为公式放大预览。

  • 加载脚本文件

    我使用了 cloudflare 的 cdn: /mathjax/2.7.4/MathJax.js,目前好像没有被墙。
    如果访问速度过慢,还请在评论里面告知一声,非常感谢!

  • 遍历 <code></code> 并调用 MathJax.Hub.Typeset() 手动排版

    这里有一个问题是:我文章中的代码也是显示在 <code></code> 中的,需要避免。不细说了,具体代码如下:

    MathJax.Hub.Typeset($('<p>$a$</p>').get(0), function(){
        console.log('typeset warming-up');
        $('code:not([class*="lang"])').each(function(_, e) {
            var html = $(e).html();
            if(html.startsWith('$') && html.endsWith('$')) {
                var wrap = $(html.startsWith('$$') ? '<div/>' : '<span/>')
                    .css('margin', '3px')
                    .html(html)[0];
                MathJax.Hub.Typeset(wrap);
                console.log('Typeset: ', e);
                $(e).replaceWith(wrap);
            }
    });
    

公式效果展示

  • $y=x^2$
  • $x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$
  • $\cos(θ+φ)=\cos(θ)\cos(φ)−\sin(θ)\sin(φ)$
  • $\int_D ({\nabla\cdot} F)dV=\int_{\partial D} F\cdot ndS$
  • $\vec{\nabla} \times \vec{F} = \left( \frac{\partial F_z}{\partial y} - \frac{\partial F_y}{\partial z} \right) \mathbf{i} + \left( \frac{\partial F_x}{\partial z} - \frac{\partial F_z}{\partial x} \right) \mathbf{j} + \left( \frac{\partial F_y}{\partial x} - \frac{\partial F_x}{\partial y} \right) \mathbf{k}$
  • $\sigma = \sqrt{ \frac{1}{N} \sum_{i=1}^N (x_i -\mu)^2}$
  • $(\nabla_X Y)^k = X^i (\nabla_i Y)^k = X^i \left( \frac{\partial Y^k}{\partial x^i} + \Gamma_{im}^k Y^m \right)$

结束语

由于我没有使用默认的自动排版,所以我的使用方式看起来非常复杂。但是,最终效果还是很不错的。

文章并没有提到其它的很多细节问题,如果有什么自定义相关的问题,欢迎一起讨论。

另外,评论中暂时不支持显示公式,但是可以插入。

这篇文章的内容已被作者标记为“过时”/“需要更新”/“不具参考意义”。

标签:博客日志 · 数学