简体   繁体   English

如何对 svg 元素进行分组

[英]How to group svg elements

I am displaying bar chart.我正在显示条形图。 Each bar is combination of a rect and a circle element.每个条都是一个矩形和一个圆形元素的组合。 Now, I want to group each rect, circle into a group element like this.现在,我想将每个矩形、圆圈组合成这样的组元素

<g class="bar_group">
  <rect class="bar" ..></rect>
  <circle class="rounded_edge" ..></circle>
 </g>
<g class="bar_group">
  <rect class="bar" ..></rect>
  <circle class="rounded_edge" ..></circle>
 </g> 

How to group them like above ie one group for a rect and circle pair only?如何像上面那样对它们进行分组,即一组仅用于矩形和圆形对? right now it is coming as all rects and circles in one group only.现在它仅作为一组中的所有矩形和圆圈出现。 ie a group under which all rects and circles.即所有矩形和圆圈下的一组。

 var data = [ { type: "s1", value: 4, }, { type: "s2", value: 2, }, { type: "s3", value: 5, }, ]; var margin = { top: 50, right: 20, bottom: 0, left: 30 }; var width = 300; var height = 220; var innerHeight = height - margin.top - 10; var barWidth = 20; var barMidpoint = barWidth / 2; var svg = d3.select("svg").attr("class", "d3svg").attr("width", width).attr("height", height); var svgG = svg.append("g").attr("transform", "translate( -6, 0)"); // X AXIS CODE var xScale = d3.scaleBand().range([margin.left, width]).padding(0.4); xScale.domain( data.map(function (d) { return d.type; }) ); var xAxis = d3.axisBottom(xScale); // call x axis svgG.append("g").attr("transform", `translate(20,${innerHeight})`).call(xAxis); // Y AXIS CODE var yScale = d3.scaleLinear().range([innerHeight, margin.top]); yScale.domain([ 0, d3.max(data, function (d) { return d.value; }), ]); var yAxis = d3.axisLeft(yScale).tickFormat(function (d) { return d; }); svgG.append("g").attr("class", "y-axis").attr("transform", `translate(${margin.left + 25},0)`).call(yAxis); // DISPLAY BARS svgG.selectAll(".bar").data(data).enter().append("rect").attr("class", "bar").attr("x", function (d) { return xScale(d.type) + 28; }).attr("y", function (d) { return yScale(d.value); }).attr("width", barWidth).attr("height", function (d) { return innerHeight - yScale(d.value); }); svgG.selectAll(".circle").data(data).enter().append("circle").attr("class", "bar").attr("cx", function (d) { return xScale(d.type) + barMidpoint + 28; }).attr("cy", function (d) { return yScale(d.value); }).attr("r", barMidpoint);
 .bar { fill: steelblue; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg width="600" height="500"></svg>

Sought Solution寻求解决方案

You currently do two enter cycles, one for the rect s and one for the circle s.您当前执行两个输入循环,一个用于rect s,一个用于circle s。 For each you append to a common parent ( svgG ).对于每个您 append 到一个共同的父母( svgG )。

Instead, in order to achieve your goal, "to group each rect, circle into a group element", we can do one enter cycle for a parent g :相反,为了实现您的目标,“将每个矩形、圆圈组合成一个组元素”,我们可以为父g做一个输入循环:

  // Create groups:
  var groups = svgG
    .selectAll(".group")
    .data(data)
    .enter()
    .append("g")
    .attr("class", "group")
    .attr("transform", function (d) {
      // will ease placement of circles/rects:
      return "translate("+(xScale(d.type) + 28)+","+ yScale(d.value) +")";
    })

  // append child elements, one of each per parent g:
  groups.append("circle")
    .attr("cx", barMidpoint)
    .attr("r", barMidpoint);

  groups.append("rect")
    .attr("width", barWidth)
    .attr("height", function (d) {
      return innerHeight - yScale(d.value);
    });

We have one g per item in the data array.我们在数据数组中的每个项目都有一个g Also, the datum of the parent is carried forward to the children, so you can still use function(d) when setting properties.此外,父项的数据会传递给子项,因此您在设置属性时仍然可以使用function(d)

We also simplify the styling of the data and the positioning by applying style and positional properties to the g .我们还通过将样式和位置属性应用于g来简化数据的样式和定位。

 var data = [ { type: "s1", value: 4, }, { type: "s2", value: 2, }, { type: "s3", value: 5, }, ]; var margin = { top: 50, right: 20, bottom: 0, left: 30 }; var width = 300; var height = 220; var innerHeight = height - margin.top - 10; var barWidth = 20; var barMidpoint = barWidth / 2; var svg = d3.select("svg").attr("class", "d3svg").attr("width", width).attr("height", height); var svgG = svg.append("g").attr("transform", "translate( -6, 0)"); // X AXIS CODE var xScale = d3.scaleBand().range([margin.left, width]).padding(0.4); xScale.domain( data.map(function (d) { return d.type; }) ); var xAxis = d3.axisBottom(xScale); // call x axis svgG.append("g").attr("transform", `translate(20,${innerHeight})`).call(xAxis); // Y AXIS CODE var yScale = d3.scaleLinear().range([innerHeight, margin.top]); yScale.domain([ 0, d3.max(data, function (d) { return d.value; }), ]); var yAxis = d3.axisLeft(yScale).tickFormat(function (d) { return d; }); svgG.append("g").attr("class", "y-axis").attr("transform", `translate(${margin.left + 25},0)`).call(yAxis); // Create groups: var groups = svgG.selectAll(".group").data(data).enter().append("g").attr("class", "group").attr("transform", function (d) { // will ease placement of circles/rects: return "translate("+(xScale(d.type) + 28)+","+ yScale(d.value) +")"; }) groups.append("circle").attr("cx", barMidpoint).attr("r", barMidpoint); groups.append("rect").attr("width", barWidth).attr("height", function (d) { return innerHeight - yScale(d.value); });
 .group { fill: steelblue; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg width="600" height="500"></svg>

Better Solution更好的解决方案

The better solution is to use one path to draw your rounded shape, such as done here .更好的解决方案是使用一条路径来绘制圆形,例如在此处完成。 We build a path generator to draw the shape, and then use that to create the path:我们构建一个路径生成器来绘制形状,然后使用它来创建路径:

 svgG.selectAll(".bar")
  .data(data)
  .enter()
  .append("path")
  .attr("d", function(d) {
        pathGeneratorFunction(params);
   });

This way we avoid having two shapes to worry about when we are only representing one data point.这样,当我们只表示一个数据点时,我们就可以避免担心两种形状。

If we wanted to be fancier, we could design a function to simply take the datum of the point and return a path string with the following pattern:如果我们想变得更漂亮,我们可以设计一个 function 来简单地获取点的基准并返回具有以下模式的路径字符串:

   .attr("d", generator);

But, I'm just demonstrating the simpler version here:但是,我只是在这里演示更简单的版本:

 var data = [ { type: "s1", value: 4, }, { type: "s2", value: 2, }, { type: "s3", value: 5, }, ]; var margin = { top: 50, right: 20, bottom: 0, left: 30 }; var width = 300; var height = 220; var innerHeight = height - margin.top - 10; var barWidth = 20; var barMidpoint = barWidth / 2; var svg = d3.select("svg").attr("class", "d3svg").attr("width", width).attr("height", height); var svgG = svg.append("g").attr("transform", "translate( -6, 0)"); // X AXIS CODE var xScale = d3.scaleBand().range([margin.left, width]).padding(0.4); xScale.domain( data.map(function (d) { return d.type; }) ); var xAxis = d3.axisBottom(xScale); // call x axis svgG.append("g").attr("transform", `translate(20,${innerHeight})`).call(xAxis); // Y AXIS CODE var yScale = d3.scaleLinear().range([innerHeight, margin.top]); yScale.domain([ 0, d3.max(data, function (d) { return d.value; }), ]); var yAxis = d3.axisLeft(yScale).tickFormat(function (d) { return d; }); svgG.append("g").attr("class", "y-axis").attr("transform", `translate(${margin.left + 25},0)`).call(yAxis); // DISPLAY BARS svgG.selectAll(".bar").data(data).enter().append("path").attr("class", "bar").attr("d", function(d) { return bar(xScale(d.type) + 28, innerHeight, barWidth, yScale(d.value), barWidth/2, 1) }) function bar(x,y,w,h,r,f) { // Flag for sweep: if(f == undefined) f = 1; // x coordinates of top of arcs var x0 = x+r; var x1 = x+wr; // y coordinates of bottom of arcs var y0 = y-h+r; // assemble path: var parts = [ "M",x,y, // step 1 "L",x,y0, // step 2 "A",r,r,0,0,f,x0,yh, // step 3 "L",x1,yh, // step 4 "A",r,r,0,0,f,x+w,y0, // step 5 "L",x+w,y, // step 6 "Z" // step 7 ]; return parts.join(" "); }
 .bar { fill: steelblue; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg width="600" height="500"></svg>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM