将D3中的每个矩形和文本分组

7
我正在使用 d3.js 创建一个条形图。在每个条形图的上方,我将显示一些文本。当用户悬停在条形图上时,应该显示文本。当他们悬停离开时,文本将消失。为了做到这一点,我需要将 <text><rect> 元素分组到一个 <g> 元素中。
<g class="gbar">
    <rect x="0" y="50" width="10" height="50" />
    <text x="15" y="40">A</text>
</g>
<g class="gbar">
    <rect x="11" y="75" width="10" height="25" />
    <text x="16" y="65">B</text>
</g>
<g class="gbar">
    <rect x="22" y="25" width="10" height="75" />
    <text x="27" y="35">C</text>
</g>

因此,我可以使用 .gbar:hover rect, .gbar:hover text { ... } CSS 样式来更改 <rect><text> 元素的颜色和不透明度。对于每个数据项,我如何使用 d3.js<rect><text> 元素放入一个 <g> 元素中呢?
谢谢! 编辑:为了增加更多背景信息,这是我目前的内容...
var svg = d3.select('.mygraph')
            .append('svg')
            .attr('height', 100);

svg.selectAll('rect')
   .data(dataSet)
   .enter()
   .append('rect')
   .attr('x', calcX)
   .attr('y', calcY)
   .attr('width', 10)
   .attr('height', calcH);

svg.selectAll('text')
   .data(dataSet)
   .enter()
   .append('text')
   .text(function (d) {
       return d.Text;
   })
   .attr('x', textX)
   .attr('y', textY);

那段代码会生成:
<svg>
    <rect x="0" y="50" width="10" height="50" />
    <rect x="11" y="75" width="10" height="25" />
    <rect x="22" y="25" width="10" height="75" />
    <text x="15" y="40">A</text>
    <text x="16" y="65">B</text>
    <text x="27" y="35">C</text>
</svg>

我对 d3.js 还很陌生。


2
你目前有哪些D3代码? - Robert Longson
2个回答

12

这是标准方法。

首先,使用“enter”选择器添加<g>元素:



var groups = svg.selectAll(".groups")
    .data(dataset)
    .enter()
    .append("g")
    .attr("class", "gbar");

然后,使用所选内容将矩形和文本都添加上去:

groups.append('rect')
    .attr('x', calcX)
    .attr('y', calcY)
    .attr('width', 10)
    .attr('height', calcH);

groups.append('text')
    .text(function (d) {
        return d.Text;
    })
   .attr('x', textX)
   .attr('y', textY);

这样做,你的矩形和文本将会成为每一组都在同一个<g>元素内。

以下是一个简单的演示(非常简单的代码,充满了魔法数字)悬停在条形或文本上:

var data = d3.range(8).map(()=>~~(Math.random()*130));

var svg = d3.select("svg")

var groups = svg.selectAll(".groups")
 .data(data)
 .enter()
 .append("g")
    .attr("class", "gbar");
 
groups.append("rect")
 .attr("x", (d,i)=> i*40)
 .attr("width", 20)
 .attr("y", d=> 150 - d)
 .attr("height", d=> d)
 .attr("fill", "teal");
 
groups.append("text")
 .attr("x", (d,i)=> i*40)
 .attr("y", d=> 145 - d)
 .text(d=>d)
.gbar:hover rect{
  fill:brown;
  }

.gbar:hover text{
  fill:brown;
  font-weight:700;
  }
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>

如果您检查此代码片段创建的SVG,您会得到以下结果:

<g class="gbar">
    <rect x="0" width="20" y="142" height="8" fill="teal"></rect>
    <text x="0" y="137">8</text>
</g>
<g class="gbar">
    <rect x="40" width="20" y="136" height="14" fill="teal"></rect>
    <text x="40" y="131">14</text>
</g>
<g class="gbar">
    <rect x="80" width="20" y="89" height="61" fill="teal"></rect>
    <text x="80" y="84">61</text>
</g>
//etc...

1
在 selectAll('.groups') 中,.groups 是什么?它是 .gbar 吗? - hackwithharsha
在这种情况下,上面的代码片段对于寻找更新元素模式的人将没有帮助。因为如果选择为空,它将始终创建新元素。我说得对吗? - hackwithharsha
@HarshaVardhan 是的,但仅选择.gbar是不够的,因为那总是“输入”选择。 - Gerardo Furtado
1
当然,首先我需要加入数据,然后创建输入选择,接着更新,最后退出。如果这是正确的吗?有没有一种方法可以链式动画更新整个 g 元素的选择,而不是矩形或文本... 感谢您的快速回复... - hackwithharsha
@HarshaVardhan 是的,有方法,但是你应该将其作为一个问题来提出。 - Gerardo Furtado
显示剩余2条评论

4
我使用组“g”来解决这个问题:每对柱形和标签存储在一个组中。每次单击一个组时,在标签和柱形上执行排序函数。请保留HTML标记。

var w = 600;
var h = 250;

var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
    11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];

var xScale = d3.scale.ordinal()
    .domain(d3.range(dataset.length))
    .rangeRoundBands([0, w], 0.05);

var yScale = d3.scale.linear()
    .domain([0, d3.max(dataset)])
    .range([0, h]);


var svg = d3.select("body")
   .append("svg")
   .attr("width", w)
   .attr("height", h);


var groups = svg.selectAll("g")
      .data(dataset)
      .enter()
      .append("g");


groups.append("rect")
   .attr("x", function(d, i) {
     return xScale(i);
   })
   .attr("y", function(d) {
     return h - yScale(d);
   })
   .attr("width", xScale.rangeBand())
   .attr("height", function(d) {
     return yScale(d);
   })
   .attr("fill", function(d) {
  return "rgb(0, 0, " + (d * 10) + ")";
   })
   .on("mouseover", function() {
     d3.select(this)
      .attr("fill", "orange");
   })
   .on("mouseout", function(d) {
    d3.select(this)
      .transition()
      .duration(250)
   .attr("fill", "rgb(0, 0, " + (d * 10) + ")");
   })
   ;


 groups.append("text")
    .text(function(d) {
       return d;
    })
    .attr("text-anchor", "middle")
    .attr("x", function(d, i) {
       return xScale(i) + xScale.rangeBand() / 2;
    })
    .attr("y", function(d) {
       return h - yScale(d) + 14;
    })
    .attr("font-family", "sans-serif")
    .attr("font-size", "11px")
    .attr("fill", "white");
    groups.on("click", function(){
      sortBars();
    })
    ;


var sortBars = function() {
  svg.selectAll("rect")
     .sort(function(a, b) {
       return d3.ascending(a, b);
      })
     .transition()
     .duration(1000)
     .attr("x", function(d, i) {
        return xScale(i);});


  svg.selectAll("text")
     .sort(function(a, b) {
       return d3.ascending(a, b);
      })
     .transition()
     .duration(1000)
     .attr("x", function(d, i) {
        return xScale(i) + xScale.rangeBand() / 2;
     });

};
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接