d3.js - 基于文本宽度的文本位置

d3.js - Text position based on text width

本文关键字:文本 位置 js 于文本 d3      更新时间:2023-09-26

我正在使用d3.js来生成代表不同假设的可视化。由于假设是由不同的部分组成的,每个单词/部分都有自己的文本元素。

我想将每个文本元素的 x 位置基于前一个单词的文本宽度,包括偏移量。假设"IF x THEN y"我需要 4 个文本元素,其中"IF"具有 x=0,并且由于"IF"的宽度为 10,而我使用 5 的偏移量"x"将得到 x=15,依此类推。

我正在使用如下所示的 json 数据:

{[
    {"id" : "id0",
    "elements" : [
      {
         "text" : "IF",
         "type" : "conditional"
      },
      {
         "text" : "X",
         "type" : "variable"
      },
      {
         "text" : "THEN",
         "type" : "conditional"},
      {
         "text" : "Y",
         "type" : "variable"
      }
    ]},
   {"id" : "id1",
    "elements" : [
      {
         "text" : "IF",
         "type" : "conditional"
      },
      {
         "text" : "abc",
         "type" : "variable"
      },
      {
         "text" : "THEN",
         "type" : "conditional"},
      {
         "text" : "xyz",
         "type" : "variable"
      }
    ]}
]}

到目前为止,我用来生成文本元素的代码(每个假设都在 g 元素中是

 var svg = d3.select("#viewport")
            .append("svg")
            .attr("width", 1200)
            .attr("height", 800);
        var content = svg.append("g").attr("id", "drawing");
        var groups = content.selectAll().data(arr)
            .enter()
            .append("g")
            .attr("class", function (d) {
                return "hypothesis " + d["id"];
            })
            .each(function (d, i) {
                d3.select(this).selectAll("text")
                    .data(d["elements"])
                    .enter()
                    .append("text")
                    .attr("class", function (d) {
                        return d.type;
                    })
                    .text(function (d) {
                        return d.text;
                    })
                    .attr("font-family", "sans-serif")
                    .attr("font-size", "20px")
                    .attr("x", function (d, j) {
                        return j++ * 100;
                    })
                    .attr("y", 50 * (i + 1));
            });

设置 x 位置时,我想获取当前文本元素的宽度并将其推送到变量上,以便我可以获得下一个新的 x 坐标,而不仅仅是使用当前每个单词 100 px 的随机偏移量。

所以问题是我如何获得计算出的文本宽度(在 getBBox 或类似的东西上看过,但它对我不起作用,因为我不知道在哪里使用它们(以及如何将其应用于文本元素。或者,如果有更好的方法来创建元素,也许不是在一次运行中。

不同的元素

需要以不同的颜色设置样式,并且必须在以后做出反应,以便将鼠标悬停在上面,这就是为什么它们必须是单个文本元素的原因。

提前谢谢。

我总是使用 getComputedTextLength 来处理这些事情,尽管 getBBox 也可以工作:

.each(function(d, i) {
  var runningWidth = 0; //<-- keep a running total
  ...
  .attr("x", function(d, j) {
    var w = this.getComputedTextLength(), //<-- length of this node
        x = runningWidth; //<-- previous length to return
    runningWidth += w; //<-- total
    return x;
  })
  ...

完整代码:

<!DOCTYPE html>
<html>
<head>
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
</head>
<body>
  <div id="viewport"></div>
  <script>
    var arr =
      [{
        "id": "id0",
        "elements": [{
          "text": "IF",
          "type": "conditional"
        }, {
          "text": "X",
          "type": "variable"
        }, {
          "text": "THEN",
          "type": "conditional"
        }, {
          "text": "Y",
          "type": "variable"
        }]
      }, {
        "id": "id1",
        "elements": [{
          "text": "IF",
          "type": "conditional"
        }, {
          "text": "abc",
          "type": "variable"
        }, {
          "text": "THEN",
          "type": "conditional"
        }, {
          "text": "xyz",
          "type": "variable"
        }]
      }];
    var svg = d3.select("#viewport")
      .append("svg")
      .attr("width", 1200)
      .attr("height", 800);
    var content = svg.append("g").attr("id", "drawing");
    var groups = content.selectAll().data(arr)
      .enter()
      .append("g")
      .attr("class", function(d) {
        return "hypothesis " + d["id"];
      })
      .each(function(d, i) {
        var runningWidth = 0;
        
        d3.select(this).selectAll("text")
          .data(d["elements"])
          .enter()
          .append("text")
          .attr("class", function(d) {
            return d.type;
          })
          .text(function(d) {
            return d.text;
          })
          .attr("font-family", "sans-serif")
          .attr("font-size", "20px")
          .attr("x", function(d, j) {
            var w = this.getComputedTextLength(),
                x = runningWidth;
            runningWidth += w;
            return x;
          })
          .attr("y", 50 * (i + 1));
      });
  </script>
</body>
</html>