/ src / components / DreamContent.jsx
DreamContent.jsx
 1  chart = {
 2  
 3    // Specify the chart’s dimensions.
 4    const width = 928;
 5    const height = width;
 6  
 7    // Create the color scale.
 8    const color = d3.scaleLinear()
 9        .domain([0, 5])
10        .range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"])
11        .interpolate(d3.interpolateHcl);
12  
13    // Compute the layout.
14    const pack = data => d3.pack()
15        .size([width, height])
16        .padding(3)
17      (d3.hierarchy(data)
18        .sum(d => d.value)
19        .sort((a, b) => b.value - a.value));
20    const root = pack(data);
21  
22    // Create the SVG container.
23    const svg = d3.create("svg")
24        .attr("viewBox", `-${width / 2} -${height / 2} ${width} ${height}`)
25        .attr("width", width)
26        .attr("height", height)
27        .attr("style", `max-width: 100%; height: auto; display: block; margin: 0 -14px; background: ${color(0)}; cursor: pointer;`);
28  
29    // Append the nodes.
30    const node = svg.append("g")
31      .selectAll("circle")
32      .data(root.descendants().slice(1))
33      .join("circle")
34        .attr("fill", d => d.children ? color(d.depth) : "white")
35        .attr("pointer-events", d => !d.children ? "none" : null)
36        .on("mouseover", function() { d3.select(this).attr("stroke", "#000"); })
37        .on("mouseout", function() { d3.select(this).attr("stroke", null); })
38        .on("click", (event, d) => focus !== d && (zoom(event, d), event.stopPropagation()));
39  
40    // Append the text labels.
41    const label = svg.append("g")
42        .style("font", "10px sans-serif")
43        .attr("pointer-events", "none")
44        .attr("text-anchor", "middle")
45      .selectAll("text")
46      .data(root.descendants())
47      .join("text")
48        .style("fill-opacity", d => d.parent === root ? 1 : 0)
49        .style("display", d => d.parent === root ? "inline" : "none")
50        .text(d => d.data.name);
51  
52    // Create the zoom behavior and zoom immediately in to the initial focus node.
53    svg.on("click", (event) => zoom(event, root));
54    let focus = root;
55    let view;
56    zoomTo([focus.x, focus.y, focus.r * 2]);
57  
58    function zoomTo(v) {
59      const k = width / v[2];
60  
61      view = v;
62  
63      label.attr("transform", d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`);
64      node.attr("transform", d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`);
65      node.attr("r", d => d.r * k);
66    }
67  
68    function zoom(event, d) {
69      const focus0 = focus;
70  
71      focus = d;
72  
73      const transition = svg.transition()
74          .duration(event.altKey ? 7500 : 750)
75          .tween("zoom", d => {
76            const i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2]);
77            return t => zoomTo(i(t));
78          });
79  
80      label
81        .filter(function(d) { return d.parent === focus || this.style.display === "inline"; })
82        .transition(transition)
83          .style("fill-opacity", d => d.parent === focus ? 1 : 0)
84          .on("start", function(d) { if (d.parent === focus) this.style.display = "inline"; })
85          .on("end", function(d) { if (d.parent !== focus) this.style.display = "none"; });
86    }
87  
88    return svg.node();
89  }