我有一个d3图表,我想在悬停上显示一个工具提示,该提示显示条形部分的标签以及该条形占总条形的百分比。我最初的想法是计算要悬停的部分的高度,并找出总条形占该部分的百分比。我是通过将其添加到我的mouseover
事件(您悬停时正在记录的事件)中来做到这一点的:
d3.select(this).style("height")
这正常工作,但我不确定从这里去哪里。我似乎无法找到一种方法来获得整个条形的高度(考虑到所有部分),以便创建方程式。
该如何解决?还是有更好的方法来获取百分比?
<!DOCTYPE html>
<meta charset="utf-8">
<svg id="chart" width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var data = [
{month: "January", toys: 16591, games: 1047, books: 0, crafts: 5757},
{month: "February", toys: 42337, games: 129, books: 835, crafts: 0},
{month: "March", toys: 3385, games: 1053, books: 6260, crafts: 10},
{month: "April", toys: 353, games: 3724, books: 4038, crafts: 0}
];
var series = d3.stack()
.keys(["toys", "games", "books", "crafts"])
.offset(d3.stackOffsetDiverging)
(data);
var svg = d3.select("svg"),
margin = {top: 20, right: 30, bottom: 30, left: 60},
width = +svg.attr("width"),
height = +svg.attr("height");
var x = d3.scaleBand()
.domain(data.map(function(d) { return d.month; }))
.rangeRound([margin.left, width - margin.right])
.padding(0.1);
var y = d3.scaleLinear()
.domain([d3.min(series, stackMin), d3.max(series, stackMax)])
.rangeRound([height - margin.bottom, margin.top]);
var z = d3.scaleOrdinal(d3.schemeCategory10);
var colors = ['#000','#000','#000','#000'];
svg.append("g")
.selectAll("g")
.data(series)
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("width", x.bandwidth)
.attr("x", function(d) { return x(d.data.month); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.on("mouseover", function(d){
console.log(d3.select(this).style("height"));
var xPos = parseFloat(d3.select(this).attr("x"));
var yPos = parseFloat(d3.select(this).attr("y"));
var height = parseFloat(d3.select(this).attr("height"))
d3.select(this).attr("stroke","blue").attr("stroke-width",0.8);
svg.append("text")
.attr("x",xPos)
.attr("y",yPos +height/2)
.attr("class","tooltip")
//.text(Math.floor(d.y_pct.toFixed(2)*100) + "% of " + d.type );
})
svg.append("g")
.attr("transform", "translate(0," + y(0) + ")")
.call(d3.axisBottom(x));
svg.append("g")
.attr("transform", "translate(" + margin.left + ",0)")
.call(d3.axisLeft(y));
function stackMin(serie) {
return d3.min(serie, function(d) { return d[0]; });
}
function stackMax(serie) {
return d3.max(serie, function(d) { return d[1]; });
}
</script>
答案 0 :(得分:1)
每个矩形的基准点都包含data
属性中该列中的所有条形:
基准面还包括矩形本身的顶部和底部(开始和结束)值:d[0]
和d[1]
。
使用这个我们可以算出一个百分比:
.on("mouseover", function(d) {
var rectHeight = d[1] - d[0]; // The top and bottom vales of the rectangle.
var columnHeight = d3.sum(d3.keys(d.data),function(k) {
return +d.data[k]; // The sum of all bar rectangle heights in that column.
});
var percentHeight = rectHeight/columnHeight*100;
上面的rect height和column height的单位不是SVG单位,而是在数据集的单位中,因为我们无需缩放即可直接访问数据。
d3.sum将忽略NaN或未定义的值,一元加号运算符会将字符串强制转换为数字,如果不可能,则将返回NaN。当然,如果您有一个month或x属性是一个数字,则需要将其从总和中删除
有了这个我们得到类似的东西:
<!DOCTYPE html>
<meta charset="utf-8">
<svg id="chart" width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var data = [
{month: "January", toys: 16591, games: 1047, books: 0, crafts: 5757},
{month: "February", toys: 42337, games: 129, books: 835, crafts: 0},
{month: "March", toys: 3385, games: 1053, books: 6260, crafts: 10},
{month: "April", toys: 353, games: 3724, books: 4038, crafts: 0}
];
var series = d3.stack()
.keys(["toys", "games", "books", "crafts"])
.offset(d3.stackOffsetDiverging)
(data);
var svg = d3.select("svg"),
margin = {top: 20, right: 30, bottom: 30, left: 60},
width = +svg.attr("width"),
height = +svg.attr("height");
var x = d3.scaleBand()
.domain(data.map(function(d) { return d.month; }))
.rangeRound([margin.left, width - margin.right])
.padding(0.1);
var y = d3.scaleLinear()
.domain([d3.min(series, stackMin), d3.max(series, stackMax)])
.rangeRound([height - margin.bottom, margin.top]);
var z = d3.scaleOrdinal(d3.schemeCategory10);
var colors = ['#000','#000','#000','#000'];
svg.append("g")
.selectAll("g")
.data(series)
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("width", x.bandwidth)
.attr("x", function(d) { return x(d.data.month); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.on("mouseover", function(d){
var rectHeight = d[1] - d[0];
var columnHeight = d3.sum(d3.keys(d.data),function(k) {
return +d.data[k];
});
console.log(Math.round(rectHeight/columnHeight*100)+"%");
})
svg.append("g")
.attr("transform", "translate(0," + y(0) + ")")
.call(d3.axisBottom(x));
svg.append("g")
.attr("transform", "translate(" + margin.left + ",0)")
.call(d3.axisLeft(y));
function stackMin(serie) {
return d3.min(serie, function(d) { return d[0]; });
}
function stackMax(serie) {
return d3.max(serie, function(d) { return d[1]; });
}
</script>