-
Notifications
You must be signed in to change notification settings - Fork 0
/
icicle.js
86 lines (69 loc) · 2.39 KB
/
icicle.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* icicle.js is a reproduction of https://observablehq.com/@d3/zoomable-icicle
*/
partition = data => {
const root = d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.height - a.height || b.value - a.value);
return d3.partition()
.size([height, (root.height + 1) * width / 3])
(root);
}
color = d3.scaleOrdinal(d3.quantize(d3.interpolateRainbow, data.children.length + 1));
format = d3.format(",d");
const width = 975;
const height = 1200;
const root = partition(data);
let focus = root;
const svg = d3.select("body").append("svg")
.attr("viewBox", [0, 0, width, height])
.style("font", "10px sans-serif");
const cell = svg
.selectAll("g")
.data(root.descendants())
.join("g")
.attr("transform", d => `translate(${d.y0},${d.x0})`);
const rect = cell.append("rect")
.attr("width", d => d.y1 - d.y0 - 1)
.attr("height", d => rectHeight(d))
.attr("fill-opacity", 0.6)
.attr("fill", d => {
if (!d.depth) return "#ccc";
while (d.depth > 1) d = d.parent;
return color(d.data.name);
})
.style("cursor", "pointer")
.on("click", clicked);
const text = cell.append("text")
.style("user-select", "none")
.attr("pointer-events", "none")
.attr("x", 4)
.attr("y", 13)
.attr("fill-opacity", d => +labelVisible(d));
text.append("tspan")
.text(d => d.data.name);
const tspan = text.append("tspan")
.attr("fill-opacity", d => labelVisible(d) * 0.7)
.text(d => ` ${format(d.value)}`);
cell.append("title")
.text(d => `${d.ancestors().map(d => d.data.name).reverse().join("/")}\n${format(d.value)}`);
function clicked(event, p) {
focus = focus === p ? p = p.parent : p;
root.each(d => d.target = {
x0: (d.x0 - p.x0) / (p.x1 - p.x0) * height,
x1: (d.x1 - p.x0) / (p.x1 - p.x0) * height,
y0: d.y0 - p.y0,
y1: d.y1 - p.y0
});
const t = cell.transition().duration(750)
.attr("transform", d => `translate(${d.target.y0},${d.target.x0})`);
rect.transition(t).attr("height", d => rectHeight(d.target));
text.transition(t).attr("fill-opacity", d => +labelVisible(d.target));
tspan.transition(t).attr("fill-opacity", d => labelVisible(d.target) * 0.7);
}
function rectHeight(d) {
return d.x1 - d.x0 - Math.min(1, (d.x1 - d.x0) / 2);
}
function labelVisible(d) {
return d.y1 <= width && d.y0 >= 0 && d.x1 - d.x0 > 16;
}