我正在尝试开发一个d3甜甜圈图视觉效果,该视觉效果仅需要用户定义的百分比-介于0到100%之间。根据这个值,我希望甜甜圈的段数等于比例。例如,如果50%,则将附加甜甜圈图段的一半。同样,如果100%,则将绘制整个甜甜圈图;所有段都将被追加。我不知道如何以一种优雅的方式实现这一目标,但是我确实找到了一个粗略的解决方法,您可以在下面的代码段中看到。
var data =
[{'value':0,'interval':6.25},
{'value':6.25,'interval':6.25},
{'value':12.5,'interval':6.25},
{'value':18.75,'interval':6.25},
{'value':25,'interval':6.25},
{'value':31.25,'interval':6.25},
{'value':'none','interval':6.25},
{'value':'none','interval':6.25},
{'value':'none','interval':6.25},
{'value':'none','interval':6.25},
{'value':'none','interval':6.25},
{'value':'none','interval':6.25},
{'value':'none','interval':6.25},
{'value':'none','interval':6.25},
{'value':'none','interval':6.25},
{'value':'none','interval':6.25}];
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.linear()
.range(["#0005fd","#00fe80"]).domain([0,35]);
var explode = function(x,index) {
var offset = (index==5) ? 80:0;
var angle = (x.startAngle + x.endAngle)/2;
var xOff = Math.sin(angle)*offset;
var yOff = -Math.cos(angle)*offset;
return "translate(" +xOff+","+yOff+ ")";
}
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.layout.pie()
.padAngle(.05)
.sort(null)
.value(function(d) { return d.interval; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
//.attr('transform', explode)
.style('stroke','none')
.style("fill", function(d) {
if (d.data.value=='none') {
return 'none'
}
return color(d.data.value); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
//.text(function(d) { return d.data.age; });
svg.append('text')
.text('37.5%') // magic number
.attr('font-family','Play')
.attr('font-size','140px')
.attr('text-anchor','middle')
.attr('x',0)
.attr('y',40);
function type(d) {
d.interval = +d.interval;
return d;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
从本质上讲,正在发生的事情是带有值的条目与读为“无”的条目的比例为37.5%,这是我的魔幻数字(6个值和10个无,因此6/16 = 37.5%)。不用说这根本不可扩展。
在我的特定时刻是否有内置的方法或其他劳动强度较小的解决方案?我只希望能够将0到100之间的数字传递给函数,然后绘制出该甜甜圈部分的百分比。在我的特定情况下,我选择了6.25,因为它在美学上似乎是最令人愉悦的。
也许用透明的自定义填充来模拟具有爆炸段的效果?似乎太hacky ...
注意:版本是可选的。那就是说我不反对d3.v5解决方案,我只是使用d3.v3,因为我还没有在v5中使用甜甜圈。
答案 0 :(得分:1)
最简单的方法是在其余线段的顶部添加另一个弧。该弧可以是任意长度,因此它覆盖了所有不需要显示的段。这可以通过以下方式完成:
var percentage = .35;
g.append("path")
.attr("d", d3.svg.arc()
.endAngle(Math.PI*2)
.startAngle(percentage * Math.PI*2)
.outerRadius(radius - 10)
.innerRadius(radius - 70)
)
.attr("fill","white")
我们从结束角Math.PI * 2开始,这是一整圈,表示100%完成。然后,我们以较小的起始角度向后移动,覆盖了100%到我们所拥有的百分比之间的所有区域。
根据您想要的样式,甚至可以使它稍微透明以显示不完整的部分。
这是一个例子:
var data = d3.range(16).map(function(d) {
return { value: d*6.25, interval: 6.25 };
})
var width = 400,
height = 400,
radius = Math.min(width, height) / 2;
var color = d3.scale.linear()
.range(["#0005fd","#00fe80"]).domain([0,35]);
var explode = function(x,index) {
var offset = (index==5) ? 80:0;
var angle = (x.startAngle + x.endAngle)/2;
var xOff = Math.sin(angle)*offset;
var yOff = -Math.cos(angle)*offset;
return "translate(" +xOff+","+yOff+ ")";
}
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.layout.pie()
.padAngle(.05)
.sort(null)
.value(function(d) { return d.interval; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
//.attr('transform', explode)
.style('stroke','none')
.style("fill", function(d) {return color(d.data.value); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.text(function(d) { return d.data.age; });
// Extra arc:
var percentage = .35;
g.append("path")
.attr("d", d3.svg.arc()
.endAngle(Math.PI*2)
.startAngle(percentage * Math.PI*2)
.outerRadius(radius - 10)
.innerRadius(radius - 70)
)
.attr("fill","white")
svg.append('text')
.text(percentage * 100 + "%") // magic number
.attr('font-family','Play')
.attr('font-size','140px')
.attr('text-anchor','middle')
.attr('x',0)
.attr('y',40);
function type(d) {
d.interval = +d.interval;
return d;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
这种方法还可以轻松制作动画:
var data = d3.range(16).map(function(d) {
return { value: d*6.25, interval: 6.25 };
})
var width = 400,
height = 400,
radius = Math.min(width, height) / 2;
var color = d3.scale.linear()
.range(["#0005fd","#00fe80"]).domain([0,35]);
var explode = function(x,index) {
var offset = (index==5) ? 80:0;
var angle = (x.startAngle + x.endAngle)/2;
var xOff = Math.sin(angle)*offset;
var yOff = -Math.cos(angle)*offset;
return "translate(" +xOff+","+yOff+ ")";
}
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.layout.pie()
.padAngle(.05)
.sort(null)
.value(function(d) { return d.interval; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
//.attr('transform', explode)
.style('stroke','none')
.style("fill", function(d) {return color(d.data.value); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.text(function(d) { return d.data.age; });
// Extra arc:
var percentage = .35;
var coverArc = g.append("path")
.attr("fill","white")
var label = svg.append('text')
.text(percentage * 100 + "%") // magic number
.attr('font-family','Play')
.attr('font-size','140px')
.attr('text-anchor','middle')
.attr('x',0)
.attr('y',40);
function transition() {
coverArc
.transition()
.tween("d", function(d) {
var that = d3.select(this),
i = d3.interpolateNumber(0, percentage);
return function(t) {
that.attr("d", d3.svg.arc()
.endAngle(Math.PI*2)
.startAngle(i(t) * Math.PI*2)
.outerRadius(radius - 10)
.innerRadius(radius - 70)
)
label.text(Math.round(i(t) * 100) + "%");
};
})
.duration(1000)
// other way:
.transition()
.tween("d", function(d) {
var that = d3.select(this),
i = d3.interpolateNumber(percentage, 0);
return function(t) {
that.attr("d", d3.svg.arc()
.endAngle(Math.PI*2)
.startAngle(i(t) * Math.PI*2)
.outerRadius(radius - 10)
.innerRadius(radius - 70)
)
label.text(Math.round(i(t) * 100) + "%");
};
})
.duration(1000)
.each("end",transition);
}
transition();
function type(d) {
d.interval = +d.interval;
return d;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
答案使用d3v3,d3v4 +有一些细微的变化,特别是与弧有关,d3.svg.arc现在是d3.arc,d3.layout.pie现在是d3.pie,并且d3.scale .linear现在是d3.scaleLinear,对于第二个片段中使用的动画,transition.each现在都是transition.on:
var data = d3.range(16).map(function(d) {
return { value: d*6.25, interval: 6.25 };
})
var width = 400,
height = 400,
radius = Math.min(width, height) / 2;
var color = d3.scaleLinear()
.range(["#0005fd","#00fe80"]).domain([0,35]);
var explode = function(x,index) {
var offset = (index==5) ? 80:0;
var angle = (x.startAngle + x.endAngle)/2;
var xOff = Math.sin(angle)*offset;
var yOff = -Math.cos(angle)*offset;
return "translate(" +xOff+","+yOff+ ")";
}
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);
var pie = d3.pie()
.padAngle(.05)
.sort(null)
.value(function(d) { return d.interval; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
//.attr('transform', explode)
.style('stroke','none')
.style("fill", function(d) {return color(d.data.value); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.text(function(d) { return d.data.age; });
// Extra arc:
var percentage = .35;
var coverArc = g.append("path")
.attr("fill","white")
var label = svg.append('text')
.text(percentage * 100 + "%") // magic number
.attr('font-family','Play')
.attr('font-size','140px')
.attr('text-anchor','middle')
.attr('x',0)
.attr('y',40);
function transition() {
coverArc
.transition()
.tween("d", function(d) {
var that = d3.select(this),
i = d3.interpolateNumber(0, percentage);
return function(t) {
that.attr("d", d3.arc()
.endAngle(Math.PI*2)
.startAngle(i(t) * Math.PI*2)
.outerRadius(radius - 10)
.innerRadius(radius - 70)
)
label.text(Math.round(i(t) * 100) + "%");
};
})
.duration(1000)
// other way:
.transition()
.tween("d", function(d) {
var that = d3.select(this),
i = d3.interpolateNumber(percentage, 0);
return function(t) {
that.attr("d", d3.arc()
.endAngle(Math.PI*2)
.startAngle(i(t) * Math.PI*2)
.outerRadius(radius - 10)
.innerRadius(radius - 70)
)
label.text(Math.round(i(t) * 100) + "%");
};
})
.duration(1000)
.on("end",transition);
}
transition();
function type(d) {
d.interval = +d.interval;
return d;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<script src="http://d3js.org/d3.v5.min.js"></script>