最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - d3.js - Text position based on text width - Stack Overflow

programmeradmin0浏览0评论

I'm working with d3.js to generate a visualization that represents different hypotheses. Since the hypotheses are made of different parts , each word / part gets its own text element.

I want to base the x-position of each text element on the text width of the previous word including an offset. Having a hypothesis "IF x THEN y" i would need 4 text elements with "IF" having x=0, and since "IF" has a width of 10 and i use an offset of 5 "x" will get x=15 and so on.

I'm using json data that could look like this:

{[
    {"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"
      }
    ]}
]}

The code i am using to generate the text elements so far (each hypothesis is in a g-element is

 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));
            });

When setting the x position i want to get the width of the current text element and push it onto a variable so i can get the next new x-coordinate instead of just using a currently random offset of 100 px per word.

So the question is how can i get the calculated text width (have seen things on getBBox or similar, but it didn't work for me since i don't know where to use them) and how to apply it to the text elements. Or if there is a better way to create the elements, maybe not in a single run.

The different elements need to be styled in different colors and have to react so mouse-over later, that's why they have to be single text elements.

Thanks in advance.

I'm working with d3.js to generate a visualization that represents different hypotheses. Since the hypotheses are made of different parts , each word / part gets its own text element.

I want to base the x-position of each text element on the text width of the previous word including an offset. Having a hypothesis "IF x THEN y" i would need 4 text elements with "IF" having x=0, and since "IF" has a width of 10 and i use an offset of 5 "x" will get x=15 and so on.

I'm using json data that could look like this:

{[
    {"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"
      }
    ]}
]}

The code i am using to generate the text elements so far (each hypothesis is in a g-element is

 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));
            });

When setting the x position i want to get the width of the current text element and push it onto a variable so i can get the next new x-coordinate instead of just using a currently random offset of 100 px per word.

So the question is how can i get the calculated text width (have seen things on getBBox or similar, but it didn't work for me since i don't know where to use them) and how to apply it to the text elements. Or if there is a better way to create the elements, maybe not in a single run.

The different elements need to be styled in different colors and have to react so mouse-over later, that's why they have to be single text elements.

Thanks in advance.

Share Improve this question edited Feb 6, 2016 at 13:53 dm1988 asked Feb 5, 2016 at 15:47 dm1988dm1988 872 silver badges11 bronze badges 3
  • Wouldn't it be easier to just build a singe string/text element? .text( return d["elements"][0] + " " + d["elements"][1] + " " + d["elements"][2] + " " + d["elements"][3];) – Mark Commented Feb 5, 2016 at 17:11
  • @Mark I want to have seperate text elements so i can style them differently. I need to highlight the different types of the elements in different colors and later need them to pop up in a different font. Maybe i should add that to the question. – dm1988 Commented Feb 6, 2016 at 13:52
  • 1 How about a single <text> element with multiple <tspan> children then? You can style the <tspan> children separately and they'd position together automatically. – Robert Longson Commented Feb 6, 2016 at 15:09
Add a ment  | 

1 Answer 1

Reset to default 5

I always use getComputedTextLength for these sorts of things, although getBBox would also work:

.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;
  })
  ...

Full code:

<!DOCTYPE html>
<html>

<head>
  <script data-require="[email protected]" data-semver="3.5.3" src="//cdnjs.cloudflare./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>

发布评论

评论列表(0)

  1. 暂无评论