无法使用 D3 更改组元素内图例文本的 x 位置

Posted

技术标签:

【中文标题】无法使用 D3 更改组元素内图例文本的 x 位置【英文标题】:Unable to change x position of legend text inside a group element using D3 【发布时间】:2017-11-30 05:16:59 【问题描述】:

我正在尝试在 D3.js 中创建水平图例。我使用组元素 (g) 作为所有图例的容器,并且各个图例 (文本) 也都包含在 "g" 元素中。结果是各个图例堆叠在一起,而不是隔开。 我尝试更改图例上的 x 属性并转换/翻译。虽然 DOM 显示应用了 x 值,但图例不会移动。因此,如果 DOM 显示 legend / g 元素位于 x = 200,它仍位于 0。 我花了两天时间试图解决这个问题,并且可能查看了 50 多个示例,包括我在 StackExchange 上可以找到的任何内容。 下面的代码是我最近的尝试。它不会出现任何错误,并且 x 值会反映在 DOM 中,但元素不会移动。

我已经包含了涵盖相关位的代码(但不是所有代码)。

此处添加图例容器:

/*<note>Add container to hold legends. */
        var LegendCanvas = d3.select("svg")
            .append("g")
                .attr("class", "legend canvas")
                .attr("x", 0)
                .attr("y", 0)
                .attr("width", Width)
                .style("fill", "#ffcccc")
                .attr("transform", "translate(0,15)");

然后有一个通过 json 对象数组的循环:

        var PrevElemLength = 0;

        /*<note>Loop through each data series, call the Valueline variable and plot the line. */
        Data.forEach(function(Key, i) 
            /*<note>Add the metric line(s). */
            Svg.append("path")
                .attr("class", "line")
                .attr("data-legend",function()  return Key.OriginId )
                /*<note>Iterates through the data series objects and applies a different color to each line. */
                .style("stroke", function () 
                    return Key.color = Color(Key.UniqueName); )
                .attr("d", Valueline(Key.DataValues));

            /*<note>Add a g element to the legend container. */
            var Legend = LegendCanvas.append("g")
                .attr("class", "legend container")
                .attr("transform", function (d, i) 
                    if (i === 0) 
                        return "translate(0,0)"
                     else  
                        PrevElemLength += this.previousElementSibling.getBBox().width;
                        return "translate(" + (PrevElemLength) + ",0)"
                    
                );

            /*<note>Adds a rectangle to pre-fix each legend. */
            Legend.append("rect")
                .attr("width", 5)
                .attr("height", 5)
                .style("fill", function () 
                    return Key.color = Color(Key.UniqueName); );

            /*<note>Adds the legend text. */
            Legend.append("text")
                .attr("x", function() 
                    return this.parentNode.getBBox().width + 5;
                )
                /*.attr("y", NetHeight + (Margin.bottom/2)+ 10) */
                .attr("class", "legend text")
                .style("fill", function () 
                    return Key.color = Color(Key.UniqueName); )
                .text(Key.UniqueName);

以下是输出的屏幕截图: enter image description here 任何有关如何创建水平图例(不重叠图例)的帮助将不胜感激。克里斯

【问题讨论】:

【参考方案1】:

试试这个:

//Legend
    var legend = vis.selectAll(".legend")
        .data(color.domain())
        .enter().append("g")
        .attr("class", "legend")
        .attr("transform", function(d, i)  return "translate(0," + i * 20 + ")"; );

    legend.append("image")
        .attr("x", 890)
        .attr("y", 70)
        .attr("width", 20)
        .attr("height", 18)
        .attr("xlink:href",function (d) 
            return "../assets/images/dev/"+d+".png";
        )

    legend.append("text")
        .attr("x", 910)
        .attr("y", 78)
        .attr("dy", ".35em")
        .style("text-anchor", "start")
        .text(function(d) 
                return d;
        ); 

【讨论】:

【参考方案2】:

问题是您在设置transform 属性时使用局部变量di 作为函数参数。本地范围内的参数i 会覆盖实际变量。局部变量 i 的值将始终为零,因为没有数据绑定到该元素。

var Legend = LegendCanvas.append("g")
   .attr("class", "legend container")
   .attr("transform", function (d, i)  //Remove i
       if (i === 0) 
          return "translate(0,0)"
        else  
          PrevElemLength += this.previousElementSibling.getBBox().width;
          return "translate(" + (PrevElemLength) + ",0)"
       
  );

我还对代码进行了细微更新以进行改进。

var LegendCanvas = d3.select("svg")
  .append("g")
  .attr("class", "legend canvas")
  .attr("x", 0)
  .attr("y", 0)
  .attr("width", 500)
  .style("fill", "#ffcccc")
  .attr("transform", "translate(0,15)");
var PrevElemLength = 0;
var Data = [
  OriginId: 1,
  UniqueName: "Some Long Text 1"
, 
  OriginId: 2,
  UniqueName: "Some Long Text 2"
];
/*<note>Loop through each data series, call the Valueline variable and plot the line. */
var Color = d3.scale.category10();
Data.forEach(function(Key, i) 

  /*<note>Add a g element to the legend container. */
  var Legend = LegendCanvas.append("g")
    .attr("class", "legend container")
    .attr("transform", function() 
      if (i === 0) 
        return "translate(0,0)"
       else 
        var marginLeft = 5;
        PrevElemLength += this.previousElementSibling.getBBox().width;
        return "translate(" + (PrevElemLength + marginLeft) + ",0)"
      
    );

  /*<note>Adds a rectangle to pre-fix each legend. */
  Legend.append("rect")
    .attr("width", 5)
    .attr("height", 5)
    .style("fill", function() 
      return Key.color = Color(Key.UniqueName);
    );

  /*<note>Adds the legend text. */
  Legend.append("text")
    .attr("x", function() 
      return this.parentNode.getBBox().width + 5;
    )
    .attr("dy", "0.4em")
    .attr("class", "legend text")
    .style("fill", function() 
      return Key.color = Color(Key.UniqueName);
    )
    .text(Key.UniqueName);
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg height=500 width=500></svg>

d3的实现方式(使用数据绑定)如下

var LegendCanvas = d3.select("svg")
  .append("g")
  .attr("class", "legend canvas")
  .attr("x", 0)
  .attr("y", 0)
  .attr("width", 500)
  .style("fill", "#ffcccc")
  .attr("transform", "translate(0,15)");

var Data = [
  OriginId: 1,
  UniqueName: "Some Long Text 1"
, 
  OriginId: 2,
  UniqueName: "Some Long Text 2"
];

var Color = d3.scale.category10();

var Legend = LegendCanvas.selectAll(".legend")
  .data(Data)
  .enter()
  .append("g")
  .attr("class", "legend container");  

Legend.append("rect")
  .attr("width", 5)
  .attr("height", 5)
  .style("fill", function(Key) 
    return Key.color = Color(Key.UniqueName);
  );

Legend.append("text")
  .attr("x", function() 
    return this.parentNode.getBBox().width + 5;
  )
  .attr("dy", "0.4em")
  .attr("class", "legend text")
  .style("fill", function(Key) 
    return Key.color = Color(Key.UniqueName);
  )
  .text(function(Key) return Key.UniqueName );

var PrevElemLength = 0;
Legend.attr("transform", function(d, i) 
    if (i === 0) 
      return "translate(0,0)"
     else 
      var marginLeft = 5;
      PrevElemLength += this.previousElementSibling.getBBox().width;
      return "translate(" + (PrevElemLength + marginLeft) + ",0)"
    
  );  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width=500 height=500></svg>

【讨论】:

以上是关于无法使用 D3 更改组元素内图例文本的 x 位置的主要内容,如果未能解决你的问题,请参考以下文章

D3 如何以文本或使用图例显示我的数据集

D3 左对齐图例文本

单击后保持 NVD3 图例的更改位置

使用 d3 旋转 SVG 图像元素

D3图表图例标签重叠

R根据条件更改组中的最小值