在輕前端網頁顯示數學公式 - 使用 KaTeX
| | 0 | |
之前介紹過在 Markdown 可以直接用 LaTeX 語法寫數學公式,目前主流 Markdown 編輯軟體、套件或程式庫幾乎都有內建支援。我的部落格平台是自己開發維護,想支援只能自己來。
研究了一下,目前 LaTeX 主流 JavaScript 程式庫有兩套:MathJax 跟 KaTex,簡單總結二者差異:
- MathJax - 程式庫肥大笨重,渲染速度慢,但支援完整 LaTeX 語法,且與 React、Vue、Angular 等框架整合性較好
- KaTeX - 程式庫較輕巧,渲染速度快,但只支援部分 LaTeX 語法
KaTeX 官網有二者速度對比的動態展示,看起來挺驚悚的。我個人向來偏好簡單輕巧的解決方案,加上不會用到太複雜的語法,沒什麼懸念,就決定是 KaTeX 了。
要在既有網頁使用 KaTeX,只需引用 katex.min.css 及 katex.min.js,KaTeX 官網有針對在瀏覽器直接引用的完整說明,對我這類輕前端開發者很友善,葛萊分多加十分。
以下示範直接使用 CDN,加幾行程式生成數學公式:線上展示
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body);"></script>
<script>
</script>
</head>
<body>
<p>函式、根號 \(f(x) = \sqrt[3]{2x} + \sqrt{x-2}\),
極限 \(\lim_{x \to 0^+} \dfrac{1}{x} = \infty\) (Inline 形式)
</p>
無窮級數 (Block 形式,使用 <code>\[ \]</code> 或 <code>$$ $$</code>)
$$\mathrm{e} = \sum_{n=0}^{\infty} \dfrac{1}{n!}$$
微分
\[\frac{d}{dx} x^2 = 2x\]
積分
$$\int_a^b y \: \mathrm{d}x$$
矩陣
$$
M =
\begin{bmatrix}
\frac{1}{2} & 0 \\
0 & -1
\end{bmatrix}
\begin{bmatrix}
1 & 0 \\
0 & 1
\end{bmatrix}
$$
矩陣(...)
$$
A_{m,n} =
\begin{pmatrix}
a_{1,1} & a_{1,2} & \cdots & a_{1,n} \\
a_{2,1} & a_{2,2} & \cdots & a_{2,n} \\
\vdots & \vdots & \ddots & \vdots \\
a_{m,1} & a_{m,2} & \cdots & a_{m,n}
\end{pmatrix}
$$
</body>
</html>
官方範例使用 defer 非同步載入,再呼叫社群程式庫(auto-render.min.js)的 renderMathInElement() 掃描網頁中的 LaTeX 語法,自動轉成數學公式。識別依據是尋找用 $$...$$
或 \[ ... \]
包夾的文字轉成區塊,用 \(...\)
包夾的內容則會以 Inline 方式穿插在原有文字內容中。另一種常見的 Inline 標示法 $...$
因為太容易誤判,預設不啟用。而掃描範圍會排除 script, noscript, style, textarea, pre, code, option 等元素。要完整掌握 renderMathInElement() 行為模式,最好的做法是看原始碼,所有的疑問馬上都能獲得解答:
const renderMathInElement = function(elem, options) {
if (!elem) {
throw new Error("No element provided to render");
}
const optionsCopy = {};
// Object.assign(optionsCopy, option)
for (const option in options) {
if (options.hasOwnProperty(option)) {
optionsCopy[option] = options[option];
}
}
// default options
optionsCopy.delimiters = optionsCopy.delimiters || [
{left: "$$", right: "$$", display: true},
{left: "\\(", right: "\\)", display: false},
// LaTeX uses $…$, but it ruins the display of normal `$` in text:
// {left: "$", right: "$", display: false},
// $ must come after $$
// Render AMS environments even if outside $$…$$ delimiters.
{left: "\\begin{equation}", right: "\\end{equation}", display: true},
{left: "\\begin{align}", right: "\\end{align}", display: true},
{left: "\\begin{alignat}", right: "\\end{alignat}", display: true},
{left: "\\begin{gather}", right: "\\end{gather}", display: true},
{left: "\\begin{CD}", right: "\\end{CD}", display: true},
{left: "\\[", right: "\\]", display: true},
];
optionsCopy.ignoredTags = optionsCopy.ignoredTags || [
"script", "noscript", "style", "textarea", "pre", "code", "option",
];
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error;
// Enable sharing of global macros defined via `\gdef` between different
// math elements within a single call to `renderMathInElement`.
optionsCopy.macros = optionsCopy.macros || {};
renderElem(elem, optionsCopy);
};
由於我是在 Markdown 轉出結果套用 KaTeX,有機會透過特定語法格式控制轉換範圍,比自動掃描有效率並可避免誤判。我設計的做法是用 ` 或 ``` 包夾公式,配合 $ 及 $$ 標籤,範例如下:線上展示
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"></script>
<script>
function parseCodeBlockLaTeX() {
document.querySelectorAll('code').forEach((block) => {
let tex = block.textContent.replace(/^\s+|\s+$/g, '');
if (tex.match(/^\$\$.*\$\$$/s) || tex.match(/^\$.*[^$]\$$/s)) {
tex = tex.replace(/^\${1,2}|\${1,2}$/g, '');
let el = document.createElement('span');
katex.render(tex, el);
block.parentNode.replaceChild(el, block);
}
});
}
document.addEventListener('DOMContentLoaded', parseCodeBlockLaTeX);
</script>
</head>
<body>
<p>函式、根號 <code>$f(x) = \sqrt[3]{2x} + \sqrt{x-2}$</code>,
極限 <code>$\lim_{x \to 0^+} \dfrac{1}{x} = \infty$</code>
</p>
<p>
無窮級數 <code>$$\mathrm{e} = \sum_{n=0}^{\infty} \dfrac{1}{n!}$$</code>
</p>
<p>
矩陣
</p>
<p>
<code>
$$
M =
\begin{bmatrix}
\frac{1}{2} & 0 \\
0 & -1
\end{bmatrix}
\begin{bmatrix}
1 & 0 \\
0 & 1
\end{bmatrix}
$$
</code>
</p>
<p>
不轉換
</p>
<p>
<code>X$f(x)$</code>、<code>$f(x)$X</code>、<code>$$f(x)$</code>、<code>$f(x)$$</code>
</p>
</body>
</html>
掌握以上技巧,我們就能輕鬆在網頁顯示數學公式囉~
To support LaTeX in a custom Markdown blog, KaTeX is the preferred lightweight choice. Integrate it by including katex.min.css and katex.min.js from a CDN. Use renderMathInElement to auto-render LaTeX syntax, or customize with specific Markdown conventions for efficiency and accuracy.
Comments
Be the first to post a comment