如何使 d3 sunburst 从 3 点开始?
How to make d3 sunburst starting at 3 o'clock?
我正在用这个例子画旭日图,http://www.jasondavies.com/coffee-wheel/。我发现大多数旭日图从 12 点开始为 0 度。
如何更改代码以使其在 3 点钟开始?
我尝试更改代码:
x = d3.scale.linear().range([0, 2 * Math.PI]),
到
x = d3.scale.linear().range([90, 2 * Math.PI+90]),
但这行不通。
可能最简单的方法是实际旋转包含可视化的 SVG 组。
<svg>
<g transform="rotate(90)">
... // sunburst goes here
<g>
<svg>
我建议重写示例中与渲染相关的部分。
示例中使用的分区布局不处理角度和旋转,它们由示例中的代码派生。需要处理三个轮换:
- 圆弧形区域的旋转。
- 文本围绕图表中心旋转。
- 文本围绕其中心旋转。
起点
这是改编为代码片段的原始奶酪轮:
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>
旋转背景区域
代替此代码:
.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)));
})
我们需要这个:
.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)));
})
(数学。PI/2 被添加到两个 return 值)
结果是:
旋转文本
旋转文本的类似更改,请参阅下面代码段中的代码。
最终结果
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>
我正在用这个例子画旭日图,http://www.jasondavies.com/coffee-wheel/。我发现大多数旭日图从 12 点开始为 0 度。 如何更改代码以使其在 3 点钟开始?
我尝试更改代码:
x = d3.scale.linear().range([0, 2 * Math.PI]),
到
x = d3.scale.linear().range([90, 2 * Math.PI+90]),
但这行不通。
可能最简单的方法是实际旋转包含可视化的 SVG 组。
<svg>
<g transform="rotate(90)">
... // sunburst goes here
<g>
<svg>
我建议重写示例中与渲染相关的部分。
示例中使用的分区布局不处理角度和旋转,它们由示例中的代码派生。需要处理三个轮换:
- 圆弧形区域的旋转。
- 文本围绕图表中心旋转。
- 文本围绕其中心旋转。
起点
这是改编为代码片段的原始奶酪轮:
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>
旋转背景区域
代替此代码:
.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)));
})
我们需要这个:
.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)));
})
(数学。PI/2 被添加到两个 return 值)
结果是:
旋转文本
旋转文本的类似更改,请参阅下面代码段中的代码。
最终结果
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>