[英]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.