I'm drawing a sunburst chart with this example, http://www.jasondavies.com/coffee-wheel/. I find out most sunburst charts starting at 12 o'clock as 0 degree. How can I change the code to make it starts at 3 o'clock?
我正在用这个例子绘制一个日射图,http://www.jasondavies.com/coffee-wheel/。我发现大多数从12点开始的日射图都是0度。我如何更改代码,使它在3点开始?
I tried to change the code:
我试着修改代码:
x = d3.scale.linear().range([0, 2 * Math.PI]),
to
来
x = d3.scale.linear().range([90, 2 * Math.PI+90]),
but this doesn't work.
但这是行不通的。
2 个解决方案
#1
1
I would suggest rewritting parts of the example related to rendering.
我建议重写示例中与渲染相关的部分。
Partition layout used in the example does not handle angles and rotations, they are derived by the code in the example. There are three rotations to deal with:
示例中使用的分区布局不处理角度和旋转,它们由示例中的代码派生。有三次轮换要处理:
- Rotation od circular segment-like areas.
- 旋转od圆片状区域。
- Rotation of the text around center of the diagram.
- 围绕图中心的文字旋转。
- Rotation of the text around its centar.
- 文本围绕中心旋转。
Starting point
This is original cheese wheel adapted to be a code snippet:
这是原始的奶酪*改编成代码片段:
var width = 840,
height = width,
radius = width / 2,
x = d3.scale.linear().range([0, 2 * Math.PI]),
y = d3.scale.pow().exponent(1.3).domain([0, 1]).range([0, radius]),
padding = 5,
duration = 1000;
var div = d3.select("body");
div.select("img").remove();
var vis = div.append("svg")
.attr("width", width + padding * 2)
.attr("height", height + padding * 2)
.append("g")
.attr("transform", "translate(" + [radius + padding, radius + padding] + ")");
div.append("p")
.attr("id", "intro")
.text("Click to zoom!");
var partition = d3.layout.partition()
.sort(null)
.value(function(d) {
return 5.8 - d.depth;
});
var arc = d3.svg.arc()
.startAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function(d) {
return Math.max(0, d.y ? y(d.y) : d.y);
})
.outerRadius(function(d) {
return Math.max(0, y(d.y + d.dy));
});
var json = getData();
var nodes = partition.nodes({ children: json });
var path = vis.selectAll("path").data(nodes);
path.enter().append("path")
.attr("id", function(d, i) {
return "path-" + i;
})
.attr("d", arc)
.attr("fill-rule", "evenodd")
.style("fill", colour)
.on("click", click);
var text = vis.selectAll("text").data(nodes);
var textEnter = text.enter().append("text")
.style("fill-opacity", 1)
.style("fill", function(d) {
return brightness(d3.rgb(colour(d))) < 125 ? "#eee" : "#000";
})
.attr("text-anchor", function(d) {
return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
})
.attr("dy", ".2em")
.attr("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1,
angle = x(d.x + d.dx / 2) * 180 / Math.PI - 90,
rotate = angle + (multiline ? -.5 : 0);
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
})
.on("click", click);
textEnter.append("tspan")
.attr("x", 0)
.text(function(d) {
return d.depth ? d.name.split(" ")[0] : "";
});
textEnter.append("tspan")
.attr("x", 0)
.attr("dy", "1em")
.text(function(d) {
return d.depth ? d.name.split(" ")[1] || "" : "";
});
function click(d) {
path.transition()
.duration(duration)
.attrTween("d", arcTween(d));
// Somewhat of a hack as we rely on arcTween updating the scales.
text.style("visibility", function(e) {
return isParentOf(d, e) ? null : d3.select(this).style("visibility");
})
.transition()
.duration(duration)
.attrTween("text-anchor", function(d) {
return function() {
return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
};
})
.attrTween("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1;
return function() {
var angle = x(d.x + d.dx / 2) * 180 / Math.PI - 90,
rotate = angle + (multiline ? -.5 : 0);
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
};
})
.style("fill-opacity", function(e) {
return isParentOf(d, e) ? 1 : 1e-6;
})
.each("end", function(e) {
d3.select(this).style("visibility", isParentOf(d, e) ? null : "hidden");
});
};
function isParentOf(p, c) {
if (p === c) return true;
if (p.children) {
return p.children.some(function(d) {
return isParentOf(d, c);
});
}
return false;
}
function colour(d) {
if (d.children) {
// There is a maximum of two children!
var colours = d.children.map(colour),
a = d3.hsl(colours[0]),
b = d3.hsl(colours[1]);
// L*a*b* might be better here...
return d3.hsl((a.h + b.h) / 2, a.s * 1.2, a.l / 1.2);
}
return d.colour || "#fff";
}
// Interpolate the scales!
function arcTween(d) {
var my = maxY(d),
xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, my]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d) {
return function(t) {
x.domain(xd(t));
y.domain(yd(t)).range(yr(t));
return arc(d);
};
};
}
function maxY(d) {
return d.children ? Math.max.apply(Math, d.children.map(maxY)) : d.y + d.dy;
}
// http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
function brightness(rgb) {
return rgb.r * .299 + rgb.g * .587 + rgb.b * .114;
}
function getData(rgb) {
return [{
"name": "Aromas",
"children": [{
"name": "Enzymatic",
"children": [{
"name": "Flowery",
"children": [{
"name": "Floral",
"children": [{
"name": "Coffee Blossom",
"colour": "#f9f0ab"
}, {
"name": "Tea Rose",
"colour": "#e8e596"
}]
}, {
"name": "Fragrant",
"children": [{
"name": "Cardamon Caraway",
"colour": "#f0e2a3"
}, {
"name": "Coriander Seeds",
"colour": "#ede487"
}]
}]
}, {
"name": "Fruity",
"children": [{
"name": "Citrus",
"children": [{
"name": "Lemon",
"colour": "#efd580"
}, {
"name": "Apple",
"colour": "#f1cb82"
}]
}, {
"name": "Berry-like",
"children": [{
"name": "Apricot",
"colour": "#f1c298"
}, {
"name": "Blackberry",
"colour": "#e8b598"
}]
}]
}, {
"name": "Herby",
"children": [{
"name": "Alliaceous",
"children": [{
"name": "Onion",
"colour": "#d5dda1"
}, {
"name": "Garlic",
"colour": "#c9d2b5"
}]
}, {
"name": "Leguminous",
"children": [{
"name": "Cucumber",
"colour": "#aec1ad"
}, {
"name": "Garden Peas",
"colour": "#a7b8a8"
}]
}]
}]
}, {
"name": "Sugar Browning",
"children": [{
"name": "Nutty",
"children": [{
"name": "Nut-like",
"children": [{
"name": "Roasted Peanuts",
"colour": "#b49a3d"
}, {
"name": "Walnuts",
"colour": "#b28647"
}]
}, {
"name": "Malt-like",
"children": [{
"name": "Balsamic Rice",
"colour": "#a97d32"
}, {
"name": "Toast",
"colour": "#b68334"
}]
}]
}, {
"name": "Carmelly",
"children": [{
"name": "Candy-like",
"children": [{
"name": "Roasted Hazelnut",
"colour": "#d6a680"
}, {
"name": "Roasted Almond",
"colour": "#dfad70"
}]
}, {
"name": "Syrup-like",
"children": [{
"name": "Honey",
"colour": "#a2765d"
}, {
"name": "Maple Syrup",
"colour": "#9f6652"
}]
}]
}, {
"name": "Chocolatey",
"children": [{
"name": "Chocolate-like",
"children": [{
"name": "Bakers",
"colour": "#b9763f"
}, {
"name": "Dark Chocolate",
"colour": "#bf6e5d"
}]
}, {
"name": "Vanilla-like",
"children": [{
"name": "Swiss",
"colour": "#af643c"
}, {
"name": "Butter",
"colour": "#9b4c3f"
}]
}]
}]
}, {
"name": "Dry Distillation",
"children": [{
"name": "Resinous",
"children": [{
"name": "Turpeny",
"children": [{
"name": "Piney",
"colour": "#72659d"
}, {
"name": "Blackcurrant-like",
"colour": "#8a6e9e"
}]
}, {
"name": "Medicinal",
"children": [{
"name": "Camphoric",
"colour": "#8f5c85"
}, {
"name": "Cineolic",
"colour": "#934b8b"
}]
}]
}, {
"name": "Spicy",
"children": [{
"name": "Warming",
"children": [{
"name": "Cedar",
"colour": "#9d4e87"
}, {
"name": "Pepper",
"colour": "#92538c"
}]
}, {
"name": "Pungent",
"children": [{
"name": "Clove",
"colour": "#8b6397"
}, {
"name": "Thyme",
"colour": "#716084"
}]
}]
}, {
"name": "Carbony",
"children": [{
"name": "Smokey",
"children": [{
"name": "Tarry",
"colour": "#2e6093"
}, {
"name": "Pipe Tobacco",
"colour": "#3a5988"
}]
}, {
"name": "Ashy",
"children": [{
"name": "Burnt",
"colour": "#4a5072"
}, {
"name": "Charred",
"colour": "#393e64"
}]
}]
}]
}]
}, {
"name": "Tastes",
"children": [{
"name": "Bitter",
"children": [{
"name": "Pungent",
"children": [{
"name": "Creosol",
"colour": "#aaa1cc"
}, {
"name": "Phenolic",
"colour": "#e0b5c9"
}]
}, {
"name": "Harsh",
"children": [{
"name": "Caustic",
"colour": "#e098b0"
}, {
"name": "Alkaline",
"colour": "#ee82a2"
}]
}]
}, {
"name": "Salt",
"children": [{
"name": "Sharp",
"children": [{
"name": "Astringent",
"colour": "#ef91ac"
}, {
"name": "Rough",
"colour": "#eda994"
}]
}, {
"name": "Bland",
"children": [{
"name": "Neutral",
"colour": "#eeb798"
}, {
"name": "Soft",
"colour": "#ecc099"
}]
}]
}, {
"name": "Sweet",
"children": [{
"name": "Mellow",
"children": [{
"name": "Delicate",
"colour": "#f6d5aa"
}, {
"name": "Mild",
"colour": "#f0d48a"
}]
}, {
"name": "Acidy",
"children": [{
"name": "Nippy",
"colour": "#efd95f"
}, {
"name": "Piquant",
"colour": "#eee469"
}]
}]
}, {
"name": "Sour",
"children": [{
"name": "Winey",
"children": [{
"name": "Tangy",
"colour": "#dbdc7f"
}, {
"name": "Tart",
"colour": "#dfd961"
}]
}, {
"name": "Soury",
"children": [{
"name": "Hard",
"colour": "#ebe378"
}, {
"name": "Acrid",
"colour": "#f5e351"
}]
}]
}]
}];
};
body {
font-size: 1em;
font-weight: 400;
word-spacing: normal;
letter-spacing: normal;
text-transform: none;
font-family: Verdana, Myriad Web, Syntax, sans-serif;
font-size-adjust: .58;
color: #000;
background: #FFF;
line-height: 1.58em;
border-top: 0;
border-left: 0;
border-bottom: 0;
border-right: 0;
width: auto;
margin: 1.58em 5% 1.58em 8%;
padding: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Rotating background areas
Instead of this code:
而这段代码:
.startAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
we need this:
我们需要:
.startAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
(Math.PI/2 is added to the both return values)
(数学。将/2添加到两个返回值)
The result is:
其结果是:
Rotating text
Similar changes for rotating text, see the code in the snippet below..
对于旋转文本的类似更改,请参见下面代码片段。
Final result
var width = 840,
height = width,
radius = width / 2,
x = d3.scale.linear().range([0, 2 * Math.PI]),
y = d3.scale.pow().exponent(1.3).domain([0, 1]).range([0, radius]),
padding = 5,
duration = 1000;
var div = d3.select("body");
div.select("img").remove();
var vis = div.append("svg")
.attr("width", width + padding * 2)
.attr("height", height + padding * 2)
.append("g")
.attr("transform", "translate(" + [radius + padding, radius + padding] + ")");
div.append("p")
.attr("id", "intro")
.text("Click to zoom!");
var partition = d3.layout.partition()
.sort(null)
.value(function(d) {
return 5.8 - d.depth;
});
var arc = d3.svg.arc()
.startAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function(d) {
return Math.max(0, d.y ? y(d.y) : d.y);
})
.outerRadius(function(d) {
return Math.max(0, y(d.y + d.dy));
});
var json = getData();
var nodes = partition.nodes({ children: json });
var path = vis.selectAll("path").data(nodes);
path.enter().append("path")
.attr("id", function(d, i) {
return "path-" + i;
})
.attr("d", arc)
.attr("fill-rule", "evenodd")
.style("fill", colour)
.on("click", click);
var text = vis.selectAll("text").data(nodes);
var textEnter = text.enter().append("text")
.style("fill-opacity", 1)
.style("fill", function(d) {
return brightness(d3.rgb(colour(d))) < 125 ? "#eee" : "#000";
})
.attr("text-anchor", function(d) {
return ((x(d.x + d.dx / 2) > Math.PI/2)&&(x(d.x + d.dx / 2) < 3*Math.PI/2)) ? "end" : "start";
})
.attr("dy", ".2em")
.attr("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1,
angle = x(d.x + d.dx / 2) * 180 / Math.PI,
rotate = angle + (multiline ? -.5 : 0);
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + ((angle > 90) && (angle < 270) ? -180 : 0) + ")";
})
.on("click", click);
textEnter.append("tspan")
.attr("x", 0)
.text(function(d) {
return d.depth ? d.name.split(" ")[0] : "";
});
textEnter.append("tspan")
.attr("x", 0)
.attr("dy", "1em")
.text(function(d) {
return d.depth ? d.name.split(" ")[1] || "" : "";
});
function click(d) {
path.transition()
.duration(duration)
.attrTween("d", arcTween(d));
// Somewhat of a hack as we rely on arcTween updating the scales.
text.style("visibility", function(e) {
return isParentOf(d, e) ? null : d3.select(this).style("visibility");
})
.transition()
.duration(duration)
.attrTween("text-anchor", function(d) {
return function() {
return ((x(d.x + d.dx / 2) > Math.PI/2)&&(x(d.x + d.dx / 2) < 3*Math.PI/2)) ? "end" : "start";
};
})
.attrTween("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1;
return function() {
var angle = x(d.x + d.dx / 2) * 180 / Math.PI,
rotate = angle + (multiline ? -.5 : 0);
// rotate = ((angle > 2*Math.PI) ? angle-2*Math.PI : angle) + (multiline ? -.5 : 0);
// if(d.level==1) rotate = 0;
// return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + (angle > 180 ? -180 : 0) + ")";
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + ((angle > 90) && (angle < 270) ? -180 : 0) + ")";
};
})
.style("fill-opacity", function(e) {
return isParentOf(d, e) ? 1 : 1e-6;
})
.each("end", function(e) {
d3.select(this).style("visibility", isParentOf(d, e) ? null : "hidden");
});
};
function isParentOf(p, c) {
if (p === c) return true;
if (p.children) {
return p.children.some(function(d) {
return isParentOf(d, c);
});
}
return false;
}
function colour(d) {
if (d.children) {
// There is a maximum of two children!
var colours = d.children.map(colour),
a = d3.hsl(colours[0]),
b = d3.hsl(colours[1]);
// L*a*b* might be better here...
return d3.hsl((a.h + b.h) / 2, a.s * 1.2, a.l / 1.2);
}
return d.colour || "#fff";
}
// Interpolate the scales!
function arcTween(d) {
var my = maxY(d),
xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, my]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d) {
return function(t) {
x.domain(xd(t));
y.domain(yd(t)).range(yr(t));
return arc(d);
};
};
}
function maxY(d) {
return d.children ? Math.max.apply(Math, d.children.map(maxY)) : d.y + d.dy;
}
// http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
function brightness(rgb) {
return rgb.r * .299 + rgb.g * .587 + rgb.b * .114;
}
function getData(rgb) {
return [{
"name": "Aromas",
"children": [{
"name": "Enzymatic",
"children": [{
"name": "Flowery",
"children": [{
"name": "Floral",
"children": [{
"name": "Coffee Blossom",
"colour": "#f9f0ab"
}, {
"name": "Tea Rose",
"colour": "#e8e596"
}]
}, {
"name": "Fragrant",
"children": [{
"name": "Cardamon Caraway",
"colour": "#f0e2a3"
}, {
"name": "Coriander Seeds",
"colour": "#ede487"
}]
}]
}, {
"name": "Fruity",
"children": [{
"name": "Citrus",
"children": [{
"name": "Lemon",
"colour": "#efd580"
}, {
"name": "Apple",
"colour": "#f1cb82"
}]
}, {
"name": "Berry-like",
"children": [{
"name": "Apricot",
"colour": "#f1c298"
}, {
"name": "Blackberry",
"colour": "#e8b598"
}]
}]
}, {
"name": "Herby",
"children": [{
"name": "Alliaceous",
"children": [{
"name": "Onion",
"colour": "#d5dda1"
}, {
"name": "Garlic",
"colour": "#c9d2b5"
}]
}, {
"name": "Leguminous",
"children": [{
"name": "Cucumber",
"colour": "#aec1ad"
}, {
"name": "Garden Peas",
"colour": "#a7b8a8"
}]
}]
}]
}, {
"name": "Sugar Browning",
"children": [{
"name": "Nutty",
"children": [{
"name": "Nut-like",
"children": [{
"name": "Roasted Peanuts",
"colour": "#b49a3d"
}, {
"name": "Walnuts",
"colour": "#b28647"
}]
}, {
"name": "Malt-like",
"children": [{
"name": "Balsamic Rice",
"colour": "#a97d32"
}, {
"name": "Toast",
"colour": "#b68334"
}]
}]
}, {
"name": "Carmelly",
"children": [{
"name": "Candy-like",
"children": [{
"name": "Roasted Hazelnut",
"colour": "#d6a680"
}, {
"name": "Roasted Almond",
"colour": "#dfad70"
}]
}, {
"name": "Syrup-like",
"children": [{
"name": "Honey",
"colour": "#a2765d"
}, {
"name": "Maple Syrup",
"colour": "#9f6652"
}]
}]
}, {
"name": "Chocolatey",
"children": [{
"name": "Chocolate-like",
"children": [{
"name": "Bakers",
"colour": "#b9763f"
}, {
"name": "Dark Chocolate",
"colour": "#bf6e5d"
}]
}, {
"name": "Vanilla-like",
"children": [{
"name": "Swiss",
"colour": "#af643c"
}, {
"name": "Butter",
"colour": "#9b4c3f"
}]
}]
}]
}, {
"name": "Dry Distillation",
"children": [{
"name": "Resinous",
"children": [{
"name": "Turpeny",
"children": [{
"name": "Piney",
"colour": "#72659d"
}, {
"name": "Blackcurrant-like",
"colour": "#8a6e9e"
}]
}, {
"name": "Medicinal",
"children": [{
"name": "Camphoric",
"colour": "#8f5c85"
}, {
"name": "Cineolic",
"colour": "#934b8b"
}]
}]
}, {
"name": "Spicy",
"children": [{
"name": "Warming",
"children": [{
"name": "Cedar",
"colour": "#9d4e87"
}, {
"name": "Pepper",
"colour": "#92538c"
}]
}, {
"name": "Pungent",
"children": [{
"name": "Clove",
"colour": "#8b6397"
}, {
"name": "Thyme",
"colour": "#716084"
}]
}]
}, {
"name": "Carbony",
"children": [{
"name": "Smokey",
"children": [{
"name": "Tarry",
"colour": "#2e6093"
}, {
"name": "Pipe Tobacco",
"colour": "#3a5988"
}]
}, {
"name": "Ashy",
"children": [{
"name": "Burnt",
"colour": "#4a5072"
}, {
"name": "Charred",
"colour": "#393e64"
}]
}]
}]
}]
}, {
"name": "Tastes",
"children": [{
"name": "Bitter",
"children": [{
"name": "Pungent",
"children": [{
"name": "Creosol",
"colour": "#aaa1cc"
}, {
"name": "Phenolic",
"colour": "#e0b5c9"
}]
}, {
"name": "Harsh",
"children": [{
"name": "Caustic",
"colour": "#e098b0"
}, {
"name": "Alkaline",
"colour": "#ee82a2"
}]
}]
}, {
"name": "Salt",
"children": [{
"name": "Sharp",
"children": [{
"name": "Astringent",
"colour": "#ef91ac"
}, {
"name": "Rough",
"colour": "#eda994"
}]
}, {
"name": "Bland",
"children": [{
"name": "Neutral",
"colour": "#eeb798"
}, {
"name": "Soft",
"colour": "#ecc099"
}]
}]
}, {
"name": "Sweet",
"children": [{
"name": "Mellow",
"children": [{
"name": "Delicate",
"colour": "#f6d5aa"
}, {
"name": "Mild",
"colour": "#f0d48a"
}]
}, {
"name": "Acidy",
"children": [{
"name": "Nippy",
"colour": "#efd95f"
}, {
"name": "Piquant",
"colour": "#eee469"
}]
}]
}, {
"name": "Sour",
"children": [{
"name": "Winey",
"children": [{
"name": "Tangy",
"colour": "#dbdc7f"
}, {
"name": "Tart",
"colour": "#dfd961"
}]
}, {
"name": "Soury",
"children": [{
"name": "Hard",
"colour": "#ebe378"
}, {
"name": "Acrid",
"colour": "#f5e351"
}]
}]
}]
}];
};
body {
font-size: 1em;
font-weight: 400;
word-spacing: normal;
letter-spacing: normal;
text-transform: none;
font-family: Verdana, Myriad Web, Syntax, sans-serif;
font-size-adjust: .58;
color: #000;
background: #FFF;
line-height: 1.58em;
border-top: 0;
border-left: 0;
border-bottom: 0;
border-right: 0;
width: auto;
margin: 1.58em 5% 1.58em 8%;
padding: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
#2
0
Probably the simplest way is to actually rotate an SVG Group which contains the visualisation.
可能最简单的方法是旋转包含可视化的SVG组。
<svg>
<g transform="rotate(90)">
... // sunburst goes here
<g>
<svg>
#1
1
I would suggest rewritting parts of the example related to rendering.
我建议重写示例中与渲染相关的部分。
Partition layout used in the example does not handle angles and rotations, they are derived by the code in the example. There are three rotations to deal with:
示例中使用的分区布局不处理角度和旋转,它们由示例中的代码派生。有三次轮换要处理:
- Rotation od circular segment-like areas.
- 旋转od圆片状区域。
- Rotation of the text around center of the diagram.
- 围绕图中心的文字旋转。
- Rotation of the text around its centar.
- 文本围绕中心旋转。
Starting point
This is original cheese wheel adapted to be a code snippet:
这是原始的奶酪*改编成代码片段:
var width = 840,
height = width,
radius = width / 2,
x = d3.scale.linear().range([0, 2 * Math.PI]),
y = d3.scale.pow().exponent(1.3).domain([0, 1]).range([0, radius]),
padding = 5,
duration = 1000;
var div = d3.select("body");
div.select("img").remove();
var vis = div.append("svg")
.attr("width", width + padding * 2)
.attr("height", height + padding * 2)
.append("g")
.attr("transform", "translate(" + [radius + padding, radius + padding] + ")");
div.append("p")
.attr("id", "intro")
.text("Click to zoom!");
var partition = d3.layout.partition()
.sort(null)
.value(function(d) {
return 5.8 - d.depth;
});
var arc = d3.svg.arc()
.startAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function(d) {
return Math.max(0, d.y ? y(d.y) : d.y);
})
.outerRadius(function(d) {
return Math.max(0, y(d.y + d.dy));
});
var json = getData();
var nodes = partition.nodes({ children: json });
var path = vis.selectAll("path").data(nodes);
path.enter().append("path")
.attr("id", function(d, i) {
return "path-" + i;
})
.attr("d", arc)
.attr("fill-rule", "evenodd")
.style("fill", colour)
.on("click", click);
var text = vis.selectAll("text").data(nodes);
var textEnter = text.enter().append("text")
.style("fill-opacity", 1)
.style("fill", function(d) {
return brightness(d3.rgb(colour(d))) < 125 ? "#eee" : "#000";
})
.attr("text-anchor", function(d) {
return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
})
.attr("dy", ".2em")
.attr("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1,
angle = x(d.x + d.dx / 2) * 180 / Math.PI - 90,
rotate = angle + (multiline ? -.5 : 0);
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
})
.on("click", click);
textEnter.append("tspan")
.attr("x", 0)
.text(function(d) {
return d.depth ? d.name.split(" ")[0] : "";
});
textEnter.append("tspan")
.attr("x", 0)
.attr("dy", "1em")
.text(function(d) {
return d.depth ? d.name.split(" ")[1] || "" : "";
});
function click(d) {
path.transition()
.duration(duration)
.attrTween("d", arcTween(d));
// Somewhat of a hack as we rely on arcTween updating the scales.
text.style("visibility", function(e) {
return isParentOf(d, e) ? null : d3.select(this).style("visibility");
})
.transition()
.duration(duration)
.attrTween("text-anchor", function(d) {
return function() {
return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
};
})
.attrTween("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1;
return function() {
var angle = x(d.x + d.dx / 2) * 180 / Math.PI - 90,
rotate = angle + (multiline ? -.5 : 0);
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
};
})
.style("fill-opacity", function(e) {
return isParentOf(d, e) ? 1 : 1e-6;
})
.each("end", function(e) {
d3.select(this).style("visibility", isParentOf(d, e) ? null : "hidden");
});
};
function isParentOf(p, c) {
if (p === c) return true;
if (p.children) {
return p.children.some(function(d) {
return isParentOf(d, c);
});
}
return false;
}
function colour(d) {
if (d.children) {
// There is a maximum of two children!
var colours = d.children.map(colour),
a = d3.hsl(colours[0]),
b = d3.hsl(colours[1]);
// L*a*b* might be better here...
return d3.hsl((a.h + b.h) / 2, a.s * 1.2, a.l / 1.2);
}
return d.colour || "#fff";
}
// Interpolate the scales!
function arcTween(d) {
var my = maxY(d),
xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, my]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d) {
return function(t) {
x.domain(xd(t));
y.domain(yd(t)).range(yr(t));
return arc(d);
};
};
}
function maxY(d) {
return d.children ? Math.max.apply(Math, d.children.map(maxY)) : d.y + d.dy;
}
// http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
function brightness(rgb) {
return rgb.r * .299 + rgb.g * .587 + rgb.b * .114;
}
function getData(rgb) {
return [{
"name": "Aromas",
"children": [{
"name": "Enzymatic",
"children": [{
"name": "Flowery",
"children": [{
"name": "Floral",
"children": [{
"name": "Coffee Blossom",
"colour": "#f9f0ab"
}, {
"name": "Tea Rose",
"colour": "#e8e596"
}]
}, {
"name": "Fragrant",
"children": [{
"name": "Cardamon Caraway",
"colour": "#f0e2a3"
}, {
"name": "Coriander Seeds",
"colour": "#ede487"
}]
}]
}, {
"name": "Fruity",
"children": [{
"name": "Citrus",
"children": [{
"name": "Lemon",
"colour": "#efd580"
}, {
"name": "Apple",
"colour": "#f1cb82"
}]
}, {
"name": "Berry-like",
"children": [{
"name": "Apricot",
"colour": "#f1c298"
}, {
"name": "Blackberry",
"colour": "#e8b598"
}]
}]
}, {
"name": "Herby",
"children": [{
"name": "Alliaceous",
"children": [{
"name": "Onion",
"colour": "#d5dda1"
}, {
"name": "Garlic",
"colour": "#c9d2b5"
}]
}, {
"name": "Leguminous",
"children": [{
"name": "Cucumber",
"colour": "#aec1ad"
}, {
"name": "Garden Peas",
"colour": "#a7b8a8"
}]
}]
}]
}, {
"name": "Sugar Browning",
"children": [{
"name": "Nutty",
"children": [{
"name": "Nut-like",
"children": [{
"name": "Roasted Peanuts",
"colour": "#b49a3d"
}, {
"name": "Walnuts",
"colour": "#b28647"
}]
}, {
"name": "Malt-like",
"children": [{
"name": "Balsamic Rice",
"colour": "#a97d32"
}, {
"name": "Toast",
"colour": "#b68334"
}]
}]
}, {
"name": "Carmelly",
"children": [{
"name": "Candy-like",
"children": [{
"name": "Roasted Hazelnut",
"colour": "#d6a680"
}, {
"name": "Roasted Almond",
"colour": "#dfad70"
}]
}, {
"name": "Syrup-like",
"children": [{
"name": "Honey",
"colour": "#a2765d"
}, {
"name": "Maple Syrup",
"colour": "#9f6652"
}]
}]
}, {
"name": "Chocolatey",
"children": [{
"name": "Chocolate-like",
"children": [{
"name": "Bakers",
"colour": "#b9763f"
}, {
"name": "Dark Chocolate",
"colour": "#bf6e5d"
}]
}, {
"name": "Vanilla-like",
"children": [{
"name": "Swiss",
"colour": "#af643c"
}, {
"name": "Butter",
"colour": "#9b4c3f"
}]
}]
}]
}, {
"name": "Dry Distillation",
"children": [{
"name": "Resinous",
"children": [{
"name": "Turpeny",
"children": [{
"name": "Piney",
"colour": "#72659d"
}, {
"name": "Blackcurrant-like",
"colour": "#8a6e9e"
}]
}, {
"name": "Medicinal",
"children": [{
"name": "Camphoric",
"colour": "#8f5c85"
}, {
"name": "Cineolic",
"colour": "#934b8b"
}]
}]
}, {
"name": "Spicy",
"children": [{
"name": "Warming",
"children": [{
"name": "Cedar",
"colour": "#9d4e87"
}, {
"name": "Pepper",
"colour": "#92538c"
}]
}, {
"name": "Pungent",
"children": [{
"name": "Clove",
"colour": "#8b6397"
}, {
"name": "Thyme",
"colour": "#716084"
}]
}]
}, {
"name": "Carbony",
"children": [{
"name": "Smokey",
"children": [{
"name": "Tarry",
"colour": "#2e6093"
}, {
"name": "Pipe Tobacco",
"colour": "#3a5988"
}]
}, {
"name": "Ashy",
"children": [{
"name": "Burnt",
"colour": "#4a5072"
}, {
"name": "Charred",
"colour": "#393e64"
}]
}]
}]
}]
}, {
"name": "Tastes",
"children": [{
"name": "Bitter",
"children": [{
"name": "Pungent",
"children": [{
"name": "Creosol",
"colour": "#aaa1cc"
}, {
"name": "Phenolic",
"colour": "#e0b5c9"
}]
}, {
"name": "Harsh",
"children": [{
"name": "Caustic",
"colour": "#e098b0"
}, {
"name": "Alkaline",
"colour": "#ee82a2"
}]
}]
}, {
"name": "Salt",
"children": [{
"name": "Sharp",
"children": [{
"name": "Astringent",
"colour": "#ef91ac"
}, {
"name": "Rough",
"colour": "#eda994"
}]
}, {
"name": "Bland",
"children": [{
"name": "Neutral",
"colour": "#eeb798"
}, {
"name": "Soft",
"colour": "#ecc099"
}]
}]
}, {
"name": "Sweet",
"children": [{
"name": "Mellow",
"children": [{
"name": "Delicate",
"colour": "#f6d5aa"
}, {
"name": "Mild",
"colour": "#f0d48a"
}]
}, {
"name": "Acidy",
"children": [{
"name": "Nippy",
"colour": "#efd95f"
}, {
"name": "Piquant",
"colour": "#eee469"
}]
}]
}, {
"name": "Sour",
"children": [{
"name": "Winey",
"children": [{
"name": "Tangy",
"colour": "#dbdc7f"
}, {
"name": "Tart",
"colour": "#dfd961"
}]
}, {
"name": "Soury",
"children": [{
"name": "Hard",
"colour": "#ebe378"
}, {
"name": "Acrid",
"colour": "#f5e351"
}]
}]
}]
}];
};
body {
font-size: 1em;
font-weight: 400;
word-spacing: normal;
letter-spacing: normal;
text-transform: none;
font-family: Verdana, Myriad Web, Syntax, sans-serif;
font-size-adjust: .58;
color: #000;
background: #FFF;
line-height: 1.58em;
border-top: 0;
border-left: 0;
border-bottom: 0;
border-right: 0;
width: auto;
margin: 1.58em 5% 1.58em 8%;
padding: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Rotating background areas
Instead of this code:
而这段代码:
.startAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
we need this:
我们需要:
.startAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
(Math.PI/2 is added to the both return values)
(数学。将/2添加到两个返回值)
The result is:
其结果是:
Rotating text
Similar changes for rotating text, see the code in the snippet below..
对于旋转文本的类似更改,请参见下面代码片段。
Final result
var width = 840,
height = width,
radius = width / 2,
x = d3.scale.linear().range([0, 2 * Math.PI]),
y = d3.scale.pow().exponent(1.3).domain([0, 1]).range([0, radius]),
padding = 5,
duration = 1000;
var div = d3.select("body");
div.select("img").remove();
var vis = div.append("svg")
.attr("width", width + padding * 2)
.attr("height", height + padding * 2)
.append("g")
.attr("transform", "translate(" + [radius + padding, radius + padding] + ")");
div.append("p")
.attr("id", "intro")
.text("Click to zoom!");
var partition = d3.layout.partition()
.sort(null)
.value(function(d) {
return 5.8 - d.depth;
});
var arc = d3.svg.arc()
.startAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.PI/2 + Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function(d) {
return Math.max(0, d.y ? y(d.y) : d.y);
})
.outerRadius(function(d) {
return Math.max(0, y(d.y + d.dy));
});
var json = getData();
var nodes = partition.nodes({ children: json });
var path = vis.selectAll("path").data(nodes);
path.enter().append("path")
.attr("id", function(d, i) {
return "path-" + i;
})
.attr("d", arc)
.attr("fill-rule", "evenodd")
.style("fill", colour)
.on("click", click);
var text = vis.selectAll("text").data(nodes);
var textEnter = text.enter().append("text")
.style("fill-opacity", 1)
.style("fill", function(d) {
return brightness(d3.rgb(colour(d))) < 125 ? "#eee" : "#000";
})
.attr("text-anchor", function(d) {
return ((x(d.x + d.dx / 2) > Math.PI/2)&&(x(d.x + d.dx / 2) < 3*Math.PI/2)) ? "end" : "start";
})
.attr("dy", ".2em")
.attr("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1,
angle = x(d.x + d.dx / 2) * 180 / Math.PI,
rotate = angle + (multiline ? -.5 : 0);
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + ((angle > 90) && (angle < 270) ? -180 : 0) + ")";
})
.on("click", click);
textEnter.append("tspan")
.attr("x", 0)
.text(function(d) {
return d.depth ? d.name.split(" ")[0] : "";
});
textEnter.append("tspan")
.attr("x", 0)
.attr("dy", "1em")
.text(function(d) {
return d.depth ? d.name.split(" ")[1] || "" : "";
});
function click(d) {
path.transition()
.duration(duration)
.attrTween("d", arcTween(d));
// Somewhat of a hack as we rely on arcTween updating the scales.
text.style("visibility", function(e) {
return isParentOf(d, e) ? null : d3.select(this).style("visibility");
})
.transition()
.duration(duration)
.attrTween("text-anchor", function(d) {
return function() {
return ((x(d.x + d.dx / 2) > Math.PI/2)&&(x(d.x + d.dx / 2) < 3*Math.PI/2)) ? "end" : "start";
};
})
.attrTween("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1;
return function() {
var angle = x(d.x + d.dx / 2) * 180 / Math.PI,
rotate = angle + (multiline ? -.5 : 0);
// rotate = ((angle > 2*Math.PI) ? angle-2*Math.PI : angle) + (multiline ? -.5 : 0);
// if(d.level==1) rotate = 0;
// return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + (angle > 180 ? -180 : 0) + ")";
return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + ((angle > 90) && (angle < 270) ? -180 : 0) + ")";
};
})
.style("fill-opacity", function(e) {
return isParentOf(d, e) ? 1 : 1e-6;
})
.each("end", function(e) {
d3.select(this).style("visibility", isParentOf(d, e) ? null : "hidden");
});
};
function isParentOf(p, c) {
if (p === c) return true;
if (p.children) {
return p.children.some(function(d) {
return isParentOf(d, c);
});
}
return false;
}
function colour(d) {
if (d.children) {
// There is a maximum of two children!
var colours = d.children.map(colour),
a = d3.hsl(colours[0]),
b = d3.hsl(colours[1]);
// L*a*b* might be better here...
return d3.hsl((a.h + b.h) / 2, a.s * 1.2, a.l / 1.2);
}
return d.colour || "#fff";
}
// Interpolate the scales!
function arcTween(d) {
var my = maxY(d),
xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, my]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d) {
return function(t) {
x.domain(xd(t));
y.domain(yd(t)).range(yr(t));
return arc(d);
};
};
}
function maxY(d) {
return d.children ? Math.max.apply(Math, d.children.map(maxY)) : d.y + d.dy;
}
// http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
function brightness(rgb) {
return rgb.r * .299 + rgb.g * .587 + rgb.b * .114;
}
function getData(rgb) {
return [{
"name": "Aromas",
"children": [{
"name": "Enzymatic",
"children": [{
"name": "Flowery",
"children": [{
"name": "Floral",
"children": [{
"name": "Coffee Blossom",
"colour": "#f9f0ab"
}, {
"name": "Tea Rose",
"colour": "#e8e596"
}]
}, {
"name": "Fragrant",
"children": [{
"name": "Cardamon Caraway",
"colour": "#f0e2a3"
}, {
"name": "Coriander Seeds",
"colour": "#ede487"
}]
}]
}, {
"name": "Fruity",
"children": [{
"name": "Citrus",
"children": [{
"name": "Lemon",
"colour": "#efd580"
}, {
"name": "Apple",
"colour": "#f1cb82"
}]
}, {
"name": "Berry-like",
"children": [{
"name": "Apricot",
"colour": "#f1c298"
}, {
"name": "Blackberry",
"colour": "#e8b598"
}]
}]
}, {
"name": "Herby",
"children": [{
"name": "Alliaceous",
"children": [{
"name": "Onion",
"colour": "#d5dda1"
}, {
"name": "Garlic",
"colour": "#c9d2b5"
}]
}, {
"name": "Leguminous",
"children": [{
"name": "Cucumber",
"colour": "#aec1ad"
}, {
"name": "Garden Peas",
"colour": "#a7b8a8"
}]
}]
}]
}, {
"name": "Sugar Browning",
"children": [{
"name": "Nutty",
"children": [{
"name": "Nut-like",
"children": [{
"name": "Roasted Peanuts",
"colour": "#b49a3d"
}, {
"name": "Walnuts",
"colour": "#b28647"
}]
}, {
"name": "Malt-like",
"children": [{
"name": "Balsamic Rice",
"colour": "#a97d32"
}, {
"name": "Toast",
"colour": "#b68334"
}]
}]
}, {
"name": "Carmelly",
"children": [{
"name": "Candy-like",
"children": [{
"name": "Roasted Hazelnut",
"colour": "#d6a680"
}, {
"name": "Roasted Almond",
"colour": "#dfad70"
}]
}, {
"name": "Syrup-like",
"children": [{
"name": "Honey",
"colour": "#a2765d"
}, {
"name": "Maple Syrup",
"colour": "#9f6652"
}]
}]
}, {
"name": "Chocolatey",
"children": [{
"name": "Chocolate-like",
"children": [{
"name": "Bakers",
"colour": "#b9763f"
}, {
"name": "Dark Chocolate",
"colour": "#bf6e5d"
}]
}, {
"name": "Vanilla-like",
"children": [{
"name": "Swiss",
"colour": "#af643c"
}, {
"name": "Butter",
"colour": "#9b4c3f"
}]
}]
}]
}, {
"name": "Dry Distillation",
"children": [{
"name": "Resinous",
"children": [{
"name": "Turpeny",
"children": [{
"name": "Piney",
"colour": "#72659d"
}, {
"name": "Blackcurrant-like",
"colour": "#8a6e9e"
}]
}, {
"name": "Medicinal",
"children": [{
"name": "Camphoric",
"colour": "#8f5c85"
}, {
"name": "Cineolic",
"colour": "#934b8b"
}]
}]
}, {
"name": "Spicy",
"children": [{
"name": "Warming",
"children": [{
"name": "Cedar",
"colour": "#9d4e87"
}, {
"name": "Pepper",
"colour": "#92538c"
}]
}, {
"name": "Pungent",
"children": [{
"name": "Clove",
"colour": "#8b6397"
}, {
"name": "Thyme",
"colour": "#716084"
}]
}]
}, {
"name": "Carbony",
"children": [{
"name": "Smokey",
"children": [{
"name": "Tarry",
"colour": "#2e6093"
}, {
"name": "Pipe Tobacco",
"colour": "#3a5988"
}]
}, {
"name": "Ashy",
"children": [{
"name": "Burnt",
"colour": "#4a5072"
}, {
"name": "Charred",
"colour": "#393e64"
}]
}]
}]
}]
}, {
"name": "Tastes",
"children": [{
"name": "Bitter",
"children": [{
"name": "Pungent",
"children": [{
"name": "Creosol",
"colour": "#aaa1cc"
}, {
"name": "Phenolic",
"colour": "#e0b5c9"
}]
}, {
"name": "Harsh",
"children": [{
"name": "Caustic",
"colour": "#e098b0"
}, {
"name": "Alkaline",
"colour": "#ee82a2"
}]
}]
}, {
"name": "Salt",
"children": [{
"name": "Sharp",
"children": [{
"name": "Astringent",
"colour": "#ef91ac"
}, {
"name": "Rough",
"colour": "#eda994"
}]
}, {
"name": "Bland",
"children": [{
"name": "Neutral",
"colour": "#eeb798"
}, {
"name": "Soft",
"colour": "#ecc099"
}]
}]
}, {
"name": "Sweet",
"children": [{
"name": "Mellow",
"children": [{
"name": "Delicate",
"colour": "#f6d5aa"
}, {
"name": "Mild",
"colour": "#f0d48a"
}]
}, {
"name": "Acidy",
"children": [{
"name": "Nippy",
"colour": "#efd95f"
}, {
"name": "Piquant",
"colour": "#eee469"
}]
}]
}, {
"name": "Sour",
"children": [{
"name": "Winey",
"children": [{
"name": "Tangy",
"colour": "#dbdc7f"
}, {
"name": "Tart",
"colour": "#dfd961"
}]
}, {
"name": "Soury",
"children": [{
"name": "Hard",
"colour": "#ebe378"
}, {
"name": "Acrid",
"colour": "#f5e351"
}]
}]
}]
}];
};
body {
font-size: 1em;
font-weight: 400;
word-spacing: normal;
letter-spacing: normal;
text-transform: none;
font-family: Verdana, Myriad Web, Syntax, sans-serif;
font-size-adjust: .58;
color: #000;
background: #FFF;
line-height: 1.58em;
border-top: 0;
border-left: 0;
border-bottom: 0;
border-right: 0;
width: auto;
margin: 1.58em 5% 1.58em 8%;
padding: 0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
#2
0
Probably the simplest way is to actually rotate an SVG Group which contains the visualisation.
可能最简单的方法是旋转包含可视化的SVG组。
<svg>
<g transform="rotate(90)">
... // sunburst goes here
<g>
<svg>