[英]Rotate Labels in d3 sunburst v4
我嘗試使用d3 v4 sunburst來查看來自.json的一些數據我可以用我想要的標簽顯示sunburst diagramm,但是左邊的標簽是顛倒的。 我嘗試了各種方法,我在這樣或其他來源找到,但沒有什么對我有用。
繼承我的編碼
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
stroke: #fff;
}
text {
font-family: Arial, sans-serif;
font-size: 18px;
}
</style>
<body>
<script src="d3.min.js"></script>
<script src="data.js" charset="utf-8"></script>
<script language="javascript">
var width = 1200,
height = 1000,
radius = Math.min(width, height) / 2;
var x = d3.scaleLinear()
.range([0, 2 * Math.PI]);
var y = d3.scaleLinear()
.range([0, radius]);
var formatNumber = d3.format(",d");
var color = d3.scaleOrdinal(d3.schemeCategory20c);
var partition = d3.partition();
var arc = d3.arc()
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI,
x(d.x0))); })
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x1)));
})
.innerRadius(function(d) { return Math.max(0, y(d.y0)); })
.outerRadius(function(d) { return Math.max(0, y(d.y1)); });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")");
root = d3.hierarchy(root);
root.sum(function(d) { return d.size; });
svg.selectAll("path")
.data(partition(root).descendants())
.enter().append("g").attr("class", "node");
path = svg.selectAll(".node")
.append("path")
.attr("d", arc)
.style("fill", function(d) { return color((d.children ? d :
d.parent).data.name); })
.on("click", click);
text = svg.selectAll(".node")
.append("text")
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.attr("x", function(d) {
return y(d.y0);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d) {
return d.data.name === "root" ? "" : d.data.name
});
function click(d) {
//Hide text while Sunburst transitions
text.transition().attr("opacity", 0);
svg.transition()
.duration(750)
.tween("scale", function() {
var xd = d3.interpolate(x.domain(), [d.x0, d.x1]),
yd = d3.interpolate(y.domain(), [d.y0, 1]),
yr = d3.interpolate(y.range(), [d.y0 ? 20 : 0, radius]);
return function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); };
})
.selectAll("path")
.attrTween("d", function(d) { return function() { return arc(d); }; })
.on("end", function(e, i) {
// Check if the animated element's data e lies within the visible
// angle span given in d:
if (e.x0 >= d.x0 && e.x0 < d.x1) {
// get a selection of the associated text element
var arcText = d3.select(this.parentNode).select("text");
// fade in the text element and recalculate positions
arcText.transition().duration(750)
.attr("opacity", 1)
.attr("transform", function() { return "rotate(" +
computeTextRotation(e) + ")" })
.attr("x", function(d) { return y(d.y0); })
.text(function(d) {
return d.data.name === "root" ? "" : d.data.name
});
}
});
}
d3.select(self.frameElement).style("height", height + "px");
// Interpolate the scales!
function arcTween(d) {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d, i) {
return i
? function(t) { return arc(d); }
: function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); return
arc(d); };
};
}
function computeTextRotation(d) {
var angle = (x((d.x0 + d.x1)/2) - Math.PI / 2) / Math.PI * 180;
return (angle > 90 || angle < 270) ? angle : 180 + angle ;
}
</script>
這是data.json:
var root={
"id":"00000001", "name": "",
"children": [
{
"id":"00000003", "name":"#",
"children": [
{
"id":"00000003", "name":"2017",
"children": [
{
"id":"00000002", "name":"Nov","size": 12608.00},
] },
] },
{
"id":"00000010", "name":"A",
"children": [
{
"id":"00000010", "name":"2017",
"children": [
{
"id":"00000003", "name":"Dez","size": 119215.80},
{
"id":"00000004", "name":"Jul","size": 5000.00},
{
"id":"00000005", "name":"Jun","size": 45536.00},
{
"id":"00000006", "name":"Mai","size": 18500.00},
{
"id":"00000007", "name":"Nov","size": 20107.31},
{
"id":"00000008", "name":"Okt","size": 70303.00},
{
"id":"00000009", "name":"Sep","size": 11240.00},
] },
] },
{
"id":"00000018", "name":"DIR",
"children": [
{
"id":"00000018", "name":"2017",
"children": [
{
"id":"00000010", "name":"Aug","size": 705110.07},
{
"id":"00000011", "name":"Dez","size": 667101.28},
{
"id":"00000012", "name":"Jul","size": 684326.04},
{
"id":"00000013", "name":"Jun","size": 975615.11},
{
"id":"00000014", "name":"Mai","size": 625832.83},
{
"id":"00000015", "name":"Nov","size": 488444.60},
{
"id":"00000016", "name":"Okt","size": 578924.89},
{
"id":"00000017", "name":"Sep","size": 755968.14},
] },
] },
{
"id":"00000020", "name":"EU",
"children": [
{
"id":"00000020", "name":"2017",
"children": [
{
"id":"00000018", "name":"Nov","size": 505400.00},
{
"id":"00000019", "name":"Sep","size": 505400.00},
] },
] },
{
"id":"00000028", "name":"NAT",
"children": [
{
"id":"00000028", "name":"2017",
"children": [
{
"id":"00000020", "name":"Aug","size": 1882688.00},
{
"id":"00000021", "name":"Dez","size": 268861.33},
{
"id":"00000022", "name":"Jul","size": 1174708.67},
{
"id":"00000023", "name":"Jun","size": 3860969.90},
{
"id":"00000024", "name":"Mai","size": 917468.75},
{
"id":"00000025", "name":"Nov","size": 2233213.25},
{
"id":"00000026", "name":"Okt","size": 2340277.41},
{
"id":"00000027", "name":"Sep","size": 1667464.09},
] },
] },
{
"id":"00000036", "name":"X",
"children": [
{
"id":"00000036", "name":"2017",
"children": [
{
"id":"00000028", "name":"Aug","size": 249939.37},
{
"id":"00000029", "name":"Dez","size": 289363.70},
{
"id":"00000030", "name":"Jul","size": 98847.32},
{
"id":"00000031", "name":"Jun9","size": 7799.00},
{
"id":"00000032", "name":"Mai","size": 19520.00},
{
"id":"00000033", "name":"Nov","size": 1177309.62},
{
"id":"00000034", "name":"Okt","size": 224970.85},
{
"id":"00000035", "name":"Sep","size": 167309.57},
] },
] },
] }
你可以看到左側是顛倒的
這個想法是通過額外的180°文本標簽旋轉,角度在90°和270°之間(圖的左側部分)。
這是更新的演示:
<meta charset="utf-8"> <style> path { stroke: #fff; } text { font-family: Arial, sans-serif; font-size: 18px; } </style> <body> <script src="https://d3js.org/d3.v4.min.js"></script> <script language="javascript"> var root={ "id":"00000001", "name": "", "children": [ { "id":"00000003", "name":"#", "children": [ { "id":"00000003", "name":"2017", "children": [ { "id":"00000002", "name":"Nov","size": 12608.00}, ] }, ] }, { "id":"00000010", "name":"A", "children": [ { "id":"00000010", "name":"2017", "children": [ { "id":"00000003", "name":"Dez","size": 119215.80}, { "id":"00000004", "name":"Jul","size": 5000.00}, { "id":"00000005", "name":"Jun","size": 45536.00}, { "id":"00000006", "name":"Mai","size": 18500.00}, { "id":"00000007", "name":"Nov","size": 20107.31}, { "id":"00000008", "name":"Okt","size": 70303.00}, { "id":"00000009", "name":"Sep","size": 11240.00}, ] }, ] }, { "id":"00000018", "name":"DIR", "children": [ { "id":"00000018", "name":"2017", "children": [ { "id":"00000010", "name":"Aug","size": 705110.07}, { "id":"00000011", "name":"Dez","size": 667101.28}, { "id":"00000012", "name":"Jul","size": 684326.04}, { "id":"00000013", "name":"Jun","size": 975615.11}, { "id":"00000014", "name":"Mai","size": 625832.83}, { "id":"00000015", "name":"Nov","size": 488444.60}, { "id":"00000016", "name":"Okt","size": 578924.89}, { "id":"00000017", "name":"Sep","size": 755968.14}, ] }, ] }, { "id":"00000020", "name":"EU", "children": [ { "id":"00000020", "name":"2017", "children": [ { "id":"00000018", "name":"Nov","size": 505400.00}, { "id":"00000019", "name":"Sep","size": 505400.00}, ] }, ] }, { "id":"00000028", "name":"NAT", "children": [ { "id":"00000028", "name":"2017", "children": [ { "id":"00000020", "name":"Aug","size": 1882688.00}, { "id":"00000021", "name":"Dez","size": 268861.33}, { "id":"00000022", "name":"Jul","size": 1174708.67}, { "id":"00000023", "name":"Jun","size": 3860969.90}, { "id":"00000024", "name":"Mai","size": 917468.75}, { "id":"00000025", "name":"Nov","size": 2233213.25}, { "id":"00000026", "name":"Okt","size": 2340277.41}, { "id":"00000027", "name":"Sep","size": 1667464.09}, ] }, ] }, { "id":"00000036", "name":"X", "children": [ { "id":"00000036", "name":"2017", "children": [ { "id":"00000028", "name":"Aug","size": 249939.37}, { "id":"00000029", "name":"Dez","size": 289363.70}, { "id":"00000030", "name":"Jul","size": 98847.32}, { "id":"00000031", "name":"Jun9","size": 7799.00}, { "id":"00000032", "name":"Mai","size": 19520.00}, { "id":"00000033", "name":"Nov","size": 1177309.62}, { "id":"00000034", "name":"Okt","size": 224970.85}, { "id":"00000035", "name":"Sep","size": 167309.57}, ] }, ] }, ] } var width = 1200, height = 1000, radius = Math.min(width, height) / 2; var x = d3.scaleLinear() .range([0, 2 * Math.PI]); var y = d3.scaleLinear() .range([0, radius]); var formatNumber = d3.format(",d"); var color = d3.scaleOrdinal(d3.schemeCategory20c); var partition = d3.partition(); var arc = d3.arc() .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x0))); }) .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x1))); }) .innerRadius(function(d) { return Math.max(0, y(d.y0)); }) .outerRadius(function(d) { return Math.max(0, y(d.y1)); }); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")"); root = d3.hierarchy(root); root.sum(function(d) { return d.size; }); svg.selectAll("path") .data(partition(root).descendants()) .enter().append("g").attr("class", "node"); path = svg.selectAll(".node") .append("path") .attr("d", arc) .style("fill", function(d) { return color((d.children ? d : d.parent).data.name); }) .on("click", click); text = svg.selectAll(".node") .append("text") .attr("transform", function(d) { var rotation = computeTextRotation(d); var translation = y(d.y0); if (rotation > 90 && rotation < 270) { rotation = rotation + 180; translation = -translation - 13; } return ( "rotate(" + rotation + ")" + "translate(" + translation + ",0)" ); }) .attr("text-anchor", function(d) { var rotation = computeTextRotation(d); return (rotation > 90 && rotation < 270) ? "end" : "start"; }) .attr("dx", function(d) { var rotation = computeTextRotation(d); return (rotation > 90 && rotation < 270) ? -6 : 6; }) .attr("dy", ".35em") // vertical-align .text(function(d) { return d.data.name === "root" ? "" : d.data.name }); function click(d) { //Hide text while Sunburst transitions text.transition().attr("opacity", 0); svg.transition() .duration(750) .tween("scale", function() { var xd = d3.interpolate(x.domain(), [d.x0, d.x1]), yd = d3.interpolate(y.domain(), [d.y0, 1]), yr = d3.interpolate(y.range(), [d.y0 ? 20 : 0, radius]); return function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); }; }) .selectAll("path") .attrTween("d", function(d) { return function() { return arc(d); }; }) .on("end", function(e, i) { // Check if the animated element's data e lies within the visible // angle span given in d: if (e.x0 >= d.x0 && e.x0 < d.x1) { // get a selection of the associated text element var arcText = d3.select(this.parentNode).select("text"); // fade in the text element and recalculate positions arcText.transition().duration(750) .attr("opacity", 1) .attr("transform", function() { return "rotate(" + computeTextRotation(e) + ")" }) .attr("x", function(d) { return y(d.y0); }) .text(function(d) { return d.data.name === "root" ? "" : d.data.name }); } }); } d3.select(self.frameElement).style("height", height + "px"); // Interpolate the scales! function arcTween(d) { var xd = d3.interpolate(x.domain(), [dx, dx + d.dx]), yd = d3.interpolate(y.domain(), [dy, 1]), yr = d3.interpolate(y.range(), [dy ? 20 : 0, radius]); return function(d, i) { return i ? function(t) { return arc(d); } : function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); return arc(d); }; }; } function computeTextRotation(d) { var angle = (x((d.x0 + d.x1)/2) - Math.PI / 2) / Math.PI * 180; return (angle > 90 || angle < 270) ? angle : 180 + angle ; } </script>
讓我們一步一步走:
首先,我們將應用此條件額外輪換:
.attr("transform", function(d) {
var rotation = computeTextRotation(d);
if (rotation > 90 && rotation < 270)
rotation = rotation + 180;
return "rotate(" + rotation + ")";
})
但是所有標簽都在圖表的右側...
這意味着我們必須反轉左標簽的翻譯:
.attr("transform", function(d) {
var rotation = computeTextRotation(d);
var translation = y(d.y0);
if (rotation > 90 && rotation < 270) {
rotation = rotation + 180;
translation = -translation;
}
return (
"rotate(" + rotation + ")" +
"translate(" + translation + ",0)"
);
})
在這里,我更喜歡使用transform
運算符而不是x
屬性text
元素來執行translate
。
但后來文字並沒有完全處於形狀的正確位置......
這可以通過將左邊元素的標簽的text-anchor
修改為text-anchor
的末尾來修復。 我們可以使用以下屬性來完成此操作:
.attr("text-anchor", function(d) {
var rotation = computeTextRotation(d);
return (rotation > 90 && rotation < 270) ? "end" : "start";
})
最后,它仍然不完美,因為我們希望左側標簽的邊距與右側相同。 因此,我們可以調整左側項目的翻譯:
.attr("dx", function(d) {
var rotation = computeTextRotation(d);
return (rotation > 90 && rotation < 270) ? -6 : 6;
})
您也可以復制此方法以適應click
功能。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.