我有一个项目,我需要创建一个堆叠条形图。此条形图的顶部需要有圆形边框。我发现了一些关于如何圆条形图的顶部边界的文档。我的例子可以在这里找到:Rounded top corners for bar chart.
它说要添加一个属性,如下所示:
`
M${x(item.name)},${y(item.value) + ry}
a${rx},${ry} 0 0 1 ${rx},${-ry}
h${x.bandwidth() - 2 * rx}
a${rx},${ry} 0 0 1 ${rx},${ry}
v${height - y(item.value) - ry}
h${-x.bandwidth()}Z
`
您还需要声明两个变量rx
和ry
来定义角的锐度。
我面临的问题是,我不能让它与我的堆叠条形图工作。顶部堆栈需要在顶部圆角。同样,当顶部堆栈为0时,下一个堆栈需要舍入。因此,无论包含什么数据,条形图的顶部始终是四舍五入的。
我添加了一个修剪下来的片段。它包括一个按钮,以过渡到我删除(设置为零)顶部堆栈。这个片段当然也包括圆角所需的属性。但它并不圆。
this.width = 400;
this.height = 200;
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
}
this.index = 0;
this.svg = d3
.select(".canvas")
.classed("svg-container", true)
.append("svg")
.attr("class", "chart")
.attr(
"viewBox",
`0 0 ${this.width} ${this.height}`
)
.attr("preserveAspectRatio", "xMinYMin meet")
.classed("svg-content-responsive", true)
.append("g");
const scale = [0, 1200];
// set the scales
this.xScale = d3
.scaleBand()
.range([0, width])
.padding(0.3);
this.yScale = d3.scaleLinear().range([this.height, 0]);
var bars = this.svg.append("g").attr("class", "bars");
const update = data => {
const scale = [0, 1200];
// Update scales.
this.xScale.domain(data.map(d => d.key));
this.yScale.domain([scale[0], scale[1]]);
const subgroups = ["home", "work", "public"];
var color = d3
.scaleOrdinal()
.domain(subgroups)
.range(["#206BF3", "#171D2C", "#8B0000"]);
var stackData = d3.stack().keys(subgroups)(data);
const rx = 12;
const ry = 12;
// Set up transition.
const dur = 1000;
const t = d3.transition().duration(dur);
bars
.selectAll("g")
.data(stackData)
.join(
enter => enter
.append("g")
.attr("fill", d => color(d.key)),
null, // no update function
exit => {
exit
.transition()
.duration(dur / 2)
.style("fill-opacity", 0)
.remove();
}
).selectAll("rect")
.data(d => d, d => d.data.key)
.join(
enter => enter
.append("rect")
.attr("class", "bar")
.attr("x", d => {
return this.xScale(d.data.key);
})
.attr("y", () => {
return this.yScale(0);
})
.attr("height", () => {
return this.height - this.yScale(0);
})
.attr("width", this.xScale.bandwidth())
.attr(
'd',
item =>
`M${this.xScale(item.name)},${this.yScale(item.value) + ry}
a${rx},${ry} 0 0 1 ${rx},${-ry}
h${this.xScale.bandwidth() - 2 * rx}
a${rx},${ry} 0 0 1 ${rx},${ry}
v${this.height - this.yScale(item.value) - ry}
h${-this.xScale.bandwidth()}Z
`
),
null,
exit => {
exit
.transition()
.duration(dur / 2)
.style("fill-opacity", 0)
.remove();
}
)
.transition(t)
.delay((d, i) => i * 20)
.attr("x", d => this.xScale(d.data.key))
.attr("y", d => {
return this.yScale(d[1]);
})
.attr("width", this.xScale.bandwidth())
.attr("height", d => {
return this.yScale(d[0]) - this.yScale(d[1]);
});
};
const data = [
[{
key: "1",
home: 282,
work: 363,
public: 379
},
{
key: "2",
home: 232,
work: 432,
public: 0
}
],
[{
key: "1",
home: 282,
work: 363,
public: 379
},
{
key: "2",
home: 232,
work: 0,
public: 0
}
]
];
update(data[this.index]);
const swap = document.querySelector(".swap");
swap.addEventListener("click", () => {
if (this.index < 1) this.index += 1;
else this.index = 0;
update(data[this.index]);
});
<button class="swap">swap</button>
<div class="canvas"></div>
<script src="https://d3js.org/d3.v6.js"></script>
1条答案
按热度按时间pinkon5k1#
将
rect
的绘图更改为path
。所以.append("rect")
变成了.append("path")
,你不再需要属性x
,y
,height
和width
。为了只舍入顶部堆栈,可以在d3绘制最后一个子组(
d[1] - d[0] == d.data[subgroups[subgroups.length-1]]
)之前将rx
和ry
设置为12,在本例中,这是“public”,否则将它们设置为0。最后,关于你的最后一个问题:
当栈顶为0时,下一个栈需要被舍入
堆栈按顺序绘制。在绘制每个堆栈之前,找出下一个堆栈/子组是否为零,如果是,则设置
rx
和ry
= 12。要找出这一点,您需要获得当前正在绘制的子组,以便您可以确定下一个子组将是什么,并获得该子组的值。