javascript 如何在d3轴上使用MathML标签?

ldioqlga  于 2023-04-19  发布在  Java
关注(0)|答案(1)|浏览(121)

我的d3轴标签使用的是自然数,我希望它们的形式更漂亮,所以我试图通过MathJax使用MathML。
当我尝试使用MathML标记作为tickFormat的参数时,它将标记视为文本,并且实际上并不以MathML形式显示它。
在我的JavaScript中,我可以让所需的分数形式正确地显示在段落中...

d3.select('.main').append('p').html('<math><mrow><mfrac><mn>$</mn> <mn>day</mn></mfrac></mrow></math>');

...但是当我尝试将相同的文本应用到我的轴上时,就像这样...

graph.selectAll('#axisY')
     .data([null])
     .join('g')
     .attr('id', 'axisY')
     .call(d3.axisLeft(y)
             .tickFormat((d) => '<math><mrow><mfrac><mn>$</mn> <mn>day</mn></mfrac></mrow></math>')
     )
     .attr('transform', `translate(${margin.left}, 0)`);

...它像文本一样拼出所有这些标签,而不是实际显示分数。
那么,我怎样才能让tickFormat解析标签,而不是把它们当作文本来处理呢?......或者有没有更简单的方法在d3轴上有漂亮的分数?

t98cgbkg

t98cgbkg1#

tickFormat期望轴刻度标签是text元素,而您希望使用html(根据您的工作示例)。
考虑几件事来让它工作:
1.使用tickValues来控制轴上显示的值的数量-如果您保持此自动状态,则分数可能会重叠,因为从屏幕真实的空间的Angular 来看,分数的渲染只是大于小数点。
1.使用foreignObject代替text元素来表示轴上的每个刻度。一旦使用了foreignObject,就可以添加html,因此可以使用mathjax
1.十进制到最小分数的转换-有一个建议的实现here
工作示例如下-但请注意硬编码的大小和位置的调整,您将需要调整您的情况。

// prep
const a1 = [0, 0.33, 0.66, 1];
const a2 = [0, 0.25, 0.5, 0.75, 1];
const width = 500;
const height = 180;
const x = d3.scaleLinear().range([0, 200]).domain([0, 1])
const y = d3.scaleLinear().range([150, 10]).domain([0, 1])
const svg = d3.select("body").append("svg").attr("width", width).attr("height", height);

// set up axes - note setting label as ""
const axis1 = d3.axisLeft(y).tickValues(a1).tickFormat(d => "");
const axis2 = d3.axisBottom(x).tickValues(a2).tickFormat(d => "");

// render axis and mathml conversion for fraction label
const gAxis1 = svg
  .append("g")
  .attr("id", "axis1")
  .attr("transform", "translate(80, 10)")
  .style("font-size", 20)
  .call(axis1)
  .call(mathmlAxis, true);

const gAxis2 = svg
  .append("g")
  .attr("id", "axis2")
  .attr("transform", "translate(150, 90)")
  .style("font-size", 20)
  .call(axis2)
  .call(mathmlAxis, false);

function mathmlAxis(ax, vertical) {
  ax.selectAll("g")
    .append("svg:foreignObject")
    .attr("width", 40)
    .attr("height", 40)
    .attr("x", vertical ? -45 : -10)
    .attr("y", vertical ? -16 : 16)
    .append("xhtml:div")
    .html((d, i) => mathmlFractionFromDec(d));
}

// https://stackoverflow.com/questions/14002113/how-to-simplify-a-decimal-into-the-smallest-possible-fraction
function mathmlFractionFromDec(x0) {
  var eps = 1.0E-15;
  var h, h1, h2, k, k1, k2, a, x;

  x = x0;
  a = Math.floor(x);
  h1 = 1;
  k1 = 0;
  h = a;
  k = 1;

  while (x-a > eps*k*k) {
      x = 1/(x-a);
      a = Math.floor(x);
      h2 = h1; h1 = h;
      k2 = k1; k1 = k;
      h = h2 + a*h1;
      k = k2 + a*k1;
  }

  return `<math><mrow><mfrac><mn>${h}</mn><mn>${k}</mn></mfrac></mrow></math>`;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.0/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>

相关问题