【开箱即用】 npm包使用Mathjax完成数学公式的展示
前言:查找网上资料,大部分MathJax的使用都是采用CDN分发的形式,没有npm包的使用教程,对于内网开发者或者一些安全监管限制比较严重的项目不太友好。本文针对此问题展示了一个开箱即用的demo演示。
-
获取MathJax包
- 非内网开发者,直接采用npm下载安装,目前最新的版本应该是3.2.2
npm install mathjax@3
- 对于网络受限的开发者,则需要特别处理
- 首先向上级申请(你的上级或者运维部门)走对应的流程,拿到npm包;
- 拷贝到
node_modules
文件夹内; - 在
package.json
文件中加入"mathjax": "^3.2.2",
避免引入失败;
- 非内网开发者,直接采用npm下载安装,目前最新的版本应该是3.2.2
-
MathJax的引入
这里第一个坑点就来了,mathjax包没有专门的入口配置文件,需要自己写。这一步询问大模型直接给出了答案;
window.MathJax = {
tex: {
inlineMath: [
["$", "$"],
["\\(", "\\)"],
], // 行内公式选择符
displayMath: [
["$$", "$$"],
["\\[", "\\]"],
], // 段内公式选择符
},
startup: {
ready() {
window.MathJax.startup.defaultReady();
},
},
};
第二个坑点,导入的入口文件必须在引入mathjax之前,顺序很重要;
// 必须在引入mathjax前引入mathjax的配置文件
import "@/utils/mathjax";
// 使用tex-mml-svg svg格式
import "mathjax/es5/tex-mml-svg";
import "@/utils/mathjax"
此路径下的文件即为上一步配置的入口配置文件
这里以vue项目为例,直接在main.ts
文件内写入上面的导入代码,即完成全局配置。
window.MathJax.typesetPromise()
而对于在js代码中写入LaTeX 语法,或者使用了某些配置组件。则需要判断使用时机,需要在dom节点挂载之后,再调用方法完成渲染。
以虚拟长列表举例,由于视图是动态加载的,在滑动滚动条后发现,不在视图内的LaTeX语法并未被渲染成数学公式。
demo展示
需求:对部分表头加一个【?】,鼠标移入到【?】上时,展示数学公式。
代码实现如下
// 初始化执行一次
makeTitleToMathJax();
// scrollContainer是获取到的滚动条,监听滚动事件
const scrollContainer = this.$refs.table.$el.querySelector(".ag-body-horizontal-scroll-viewport");
scrollContainer.addEventListener("scroll", onScroll);
const onScroll = debounce(makeTitleToMathJax(), 300); // 防抖
function makeTitleToMathJax() {
// 对于需要渲染的表头提前添加了#mathjaxText的id,这里就直接获取到
const textList = this.$refs.table.$el.querySelectorAll("#mathjaxText");
// 给每一个元素设置鼠标移入移除事件
textList.forEach((element) => {
let currentTooltip = null;
// 鼠标移入添加提示
element.addEventListener("mouseover", (event) => {
// 在getMathJaxTemplate中每个元素都设置了一个 data-latex 属性,其中包含 LaTeX 代码
const latexCode = element.dataset.latex;
if (latexCode) {
currentTooltip = createTooltip(latexCode, event);
}
});
});
}
// 创建 tooltip 的函数
export function createTooltip(latexCode, event) {
const tooltip = document.createElement("div");
tooltip.className = "latex-tooltip";
tooltip.style.position = "absolute";
tooltip.style.padding = "5px";
tooltip.style.backgroundColor = "#f0f0f0";
tooltip.style.borderRadius = "5px";
tooltip.style.zIndex = "1000";
tooltip.style.top = `${event.pageY + 10}px`;
// 创建包含LaTeX代码的span元素
const latexSpan = document.createElement("span");
latexSpan.className = "latex-code"; // 可以给LaTeX代码添加样式类
latexSpan.textContent = latexCode;
// 将span元素添加到tooltip中
tooltip.appendChild(latexSpan);
// 将tooltip添加到文档中
document.body.appendChild(tooltip);
// 使用 MathJax 渲染 tooltip 中的 LaTeX 代码
window.MathJax.typesetPromise([tooltip]).then(() => {
const elements = tooltip.querySelectorAll(".latex-code > .MathJax");
elements.forEach((el: any) => {
el.style.textAlign = "left";
});
// 为防止产生的tooltip超出浏览器窗口,需要计算
// 获取浏览器窗口的视口宽度
const x = event.pageX + 10;
const viewportWidth =
window.innerWidth || document.documentElement.clientWidth;
const tooltipWidth = tooltip.offsetWidth;
// 检查提示框是否会超出视口右侧或底部
if (x + tooltipWidth > viewportWidth) {
// 如果超出右侧,则将提示框放在视口右侧边缘内部,保持10px的间距
tooltip.style.left = `${viewportWidth - tooltipWidth - 10}px`;
} else {
// 否则,按原位置设置
tooltip.style.left = `${event.pageX + 10}px`;
}
});
return tooltip;
}