在 d3.js 中将标签放置在节点的中心

Posted

技术标签:

【中文标题】在 d3.js 中将标签放置在节点的中心【英文标题】:Placing labels at the center of nodes in d3.js 【发布时间】:2012-08-05 03:44:04 【问题描述】:

我从 d3.js 开始,并尝试创建一行节点,每个节点都包含一个居中的数字标签。

我能够在视觉上产生所需的结果,但我这样做的方式几乎不是最优的,因为它涉及对每个文本元素的 x-y 坐标进行硬编码。下面是代码:

var svg_w = 800;
var svg_h = 400;
var svg = d3.select("body")
    .append("svg")
    .attr("width", svg_w)
    .attr("weight", svg_h);

var dataset = [];
for (var i = 0; i < 6; i++) 
    var datum = 10 + Math.round(Math.random() * 20);
    dataset.push(datum);


var nodes = svg.append("g")
               .attr("class", "nodes")
               .selectAll("circle")
               .data(dataset)
               .enter()
               .append("circle")
               .attr("class", "node")
               .attr("cx", function(d, i) 
                   return (i * 70) + 50;
               )
               .attr("cy", svg_h / 2)
               .attr("r", 20);

var labels = svg.append("g")
                .attr("class", "labels")
                .selectAll("text")
                .data(dataset)
                .enter()
                .append("text")
                .attr("dx", function(d, i) 
                    return (i * 70) + 42
                )
                .attr("dy", svg_h / 2 + 5)
                .text(function(d) 
                    return d;
                );

node 类是我为 circle 元素单独定义的自定义 CSS 类,而 nodeslabels 类没有明确定义,它们是从 answer 借用的。

正如所见,每个文本标签的位置都是硬编码的,因此它出现在每个节点的中心。显然,这不是正确的解决方案。

我的问题是,我应该如何正确地将每个文本标签与每个节点圆圈动态关联,以便标签的位置随着圆圈的位置自动变化。代码示例非常欢迎概念性解释。

【问题讨论】:

文本锚点似乎还不能直接在 D3 中工作,但您是否尝试过像 CSS 文本对齐这样简单的方法?这应该可以解决问题! w3schools.com/cs-s-ref/pr_text_text-align.asp 也可以看看来自 d3 Google Group 的类似帖子:groups.google.com/forum/#!searchin/d3-js/text-align/d3-js/… 你从哪里得到文本锚在 d3 中不起作用的想法? 几个月前他们没有工作...感谢澄清:) 很高兴看到解决这个问题的简单方法 【参考方案1】:

text-anchor 属性在 D3 创建的 svg 元素上按预期工作。但是,您需要将textcircle 附加到一个公共g 元素中,以确保textcircle 彼此居中。

为此,您可以将 nodes 变量更改为:

var nodes = svg.append("g")
           .attr("class", "nodes")
           .selectAll("circle")
           .data(dataset)
           .enter()
           // Add one g element for each data node here.
           .append("g")
           // Position the g element like the circle element used to be.
           .attr("transform", function(d, i) 
             // Set d.x and d.y here so that other elements can use it. d is 
             // expected to be an object here.
             d.x = i * 70 + 50,
             d.y = svg_h / 2;
             return "translate(" + d.x + "," + d.y + ")"; 
           );

请注意,dataset 现在是对象列表,因此可以使用 d.yd.x 而不仅仅是字符串列表。

然后,将您的 circletext 附加代码替换为以下内容:

// Add a circle element to the previously added g element.
nodes.append("circle")
      .attr("class", "node")
      .attr("r", 20);

// Add a text element to the previously added g element.
nodes.append("text")
     .attr("text-anchor", "middle")
     .text(function(d) 
       return d.name;
      );

现在,您无需更改circle 的位置,而是更改g 元素的位置,这会同时移动circletext

这是一个JSFiddle,在圆圈上显示居中的文本。

如果您想让文本位于单独的 g 元素中,以便它始终显示在顶部,则使用在第一个 g 元素的创建中设置的 d.xd.y 值到 transform文本。

var text = svg.append("svg:g").selectAll("g")
         .data(force.nodes())
         .enter().append("svg:g");

text.append("svg:text")
    .attr("text-anchor", "middle")
    .text(function(d)  return d.name; );

text.attr("transform",  function(d) 
      return "translate(" + d.x + "," + d.y + ")"; 
    );

【讨论】:

感谢您的回答!您的方法似乎类似于answer 中概述的第一种方法,这意味着每个标签将仅绘制在其圆圈的顶部,但将绘制在其他圆圈的下方。是否可以使用同一答案中概述的双数据连接方法来做到这一点? (这是我模仿我的尝试)。谢谢! 我对此进行了更新。您需要更改的两个主要内容是使您的数据成为对象列表而不是字符串列表,以便可以为每个字符串存储 yx,并将您的文本放入 g 元素中,以便g 元素可以转换。 如果我正确理解了上面更新的代码,您首先创建一个g 元素,并在其中为数组中的数据创建一系列g 元素,然后创建@987654353每个g 元素中的@ 元素?让我有点困惑的是text.append("svg:text")...,这里的text 代表g 元素的系列吗?如果是这样,D3 如何为这些g 元素中的每一个 创建一个text 元素?语法有点误导。 text 确实代表了一系列 g 元素。在 D3 的掩护下,append 方法知道在给定元素列表时执行each。因此append 应用于列表中的每个g 元素。 只是进一步观察:每个text 元素只有.attr("text-anchor", "middle"),标签在水平方向的中间位置,但在垂直方向稍微偏离。我通过添加attr("y", ".3em")(从 d3.js 网站上的示例中借用)来解决此问题,即使对于任意大小的节点圈,它似乎也能正常工作。然而,这个附加属性究竟是什么让我无法理解。当然,它对每个 text 元素的 y 坐标都有作用,但为什么特别是 .3em 呢?这对我来说几乎是神奇的......【参考方案2】:

最佳答案came from the asker本人:

只是进一步的观察:只有 .attr("text-anchor", "middle") 对于每个文本元素,标签水平位于中间,但 垂直方向稍微偏离。我通过添加 attr("y", ".3em") 解决了这个问题 (借自 d3.js 网站上的示例),这似乎运作良好 即使对于任意大小的节点圆。然而,这究竟是什么 附加属性确实使我无法理解。当然,确实如此 每个文本元素的 y 坐标,但为什么 .3em 在 特定?这对我来说几乎是神奇的......

只需将.attr("text-anchor", "middle") 添加到每个文本元素即可。

例子:

node.append("text")
    .attr("x", 0)
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .text(function(d)  return d.name; );

【讨论】:

忽略选择的答案。这是迄今为止更好的方法。 哇。好在我向下滚动到下一个答案。我不阅读所选答案的懒惰得到了回报。 使用这个时,我的 html 有文本,但它没有显示在我的实际节点中。【参考方案3】:

此页面describes what's going on under the svg hood 涉及文本元素。了解底层机制和数据结构有助于我更好地了解如何修改代码以使其正常工作。

【讨论】:

以上是关于在 d3.js 中将标签放置在节点的中心的主要内容,如果未能解决你的问题,请参考以下文章

D3.js return color() 似乎是随机的?

如何在 d3.js 中同时自动移动节点和链接

如何在 D3.js 中为网络图的矩形节点添加文本?

如何检查没有链接的节点的d3 js力图并删除它们?

如何将两个父节点连接到一个子节点以及如何务实地为树中的每个节点制作工具提示?在 D3 js (SVG) 中

d3.js v4将视口移动到特定节点