| | |
| | | async function drawGraph(baseUrl, pathColors, depth, enableDrag, enableLegend, enableZoom) { |
| | | async function drawGraph( |
| | | baseUrl, |
| | | pathColors, |
| | | depth, |
| | | enableDrag, |
| | | enableLegend, |
| | | enableZoom, |
| | | isHome, |
| | | opacityScale, |
| | | scale, |
| | | repelForce, |
| | | fontSize |
| | | ) { |
| | | const container = document.getElementById("graph-container") |
| | | const { index, links, content } = await fetchData |
| | | |
| | |
| | | .on("end", enableDrag ? dragended : noop) |
| | | } |
| | | |
| | | const height = Math.max(container.offsetHeight, 250) |
| | | const height = Math.max(container.offsetHeight, isHome ? 500 : 250) |
| | | const width = container.offsetWidth |
| | | |
| | | const simulation = d3 |
| | | .forceSimulation(data.nodes) |
| | | .force("charge", d3.forceManyBody().strength(-30)) |
| | | .force("charge", d3.forceManyBody().strength(-100 * repelForce)) |
| | | .force( |
| | | "link", |
| | | d3 |
| | |
| | | .append("svg") |
| | | .attr("width", width) |
| | | .attr("height", height) |
| | | .attr("viewBox", [-width / 2, -height / 2, width, height]) |
| | | .attr('viewBox', [-width / 2 * 1 / scale, -height / 2 * 1 / scale, width * 1 / scale, height * 1 / scale]) |
| | | |
| | | if (enableLegend) { |
| | | const legend = [{ Current: "var(--g-node-active)" }, { Note: "var(--g-node)" }, ...pathColors] |
| | |
| | | ]) |
| | | const neighbourNodes = d3.selectAll(".node").filter((d) => neighbours.includes(d.id)) |
| | | const currentId = d.id |
| | | window.Million.prefetch(new URL(`${baseUrl}${decodeURI(d.id).replace(/\s+/g, "-")}/`)) |
| | | // window.Million.prefetch(new URL(`${baseUrl}${decodeURI(d.id).replace(/\s+/g, "-")}/`)) |
| | | const linkNodes = d3 |
| | | .selectAll(".link") |
| | | .filter((d) => d.source.id === currentId || d.target.id === currentId) |
| | |
| | | // highlight links |
| | | linkNodes.transition().duration(200).attr("stroke", "var(--g-link-active)") |
| | | |
| | | const bigFont = fontSize+0.5 |
| | | |
| | | // show text for self |
| | | d3.select(this.parentNode) |
| | | .raise() |
| | | .select("text") |
| | | .transition() |
| | | .duration(200) |
| | | .style("opacity", 1) |
| | | .attr('opacityOld', d3.select(this.parentNode).select('text').style("opacity")) |
| | | .style('opacity', 1) |
| | | .style('font-size', bigFont+'em') |
| | | .attr('dy', d => nodeRadius(d) + 20 + 'px') // radius is in px |
| | | }) |
| | | .on("mouseleave", function (_, d) { |
| | | d3.selectAll(".node").transition().duration(200).attr("fill", color) |
| | |
| | | |
| | | linkNodes.transition().duration(200).attr("stroke", "var(--g-link)") |
| | | |
| | | d3.select(this.parentNode).select("text").transition().duration(200).style("opacity", 0) |
| | | d3.select(this.parentNode) |
| | | .select("text") |
| | | .transition() |
| | | .duration(200) |
| | | .style('opacity', d3.select(this.parentNode).select('text').attr("opacityOld")) |
| | | .style('font-size', fontSize+'em') |
| | | .attr('dy', d => nodeRadius(d) + 8 + 'px') // radius is in px |
| | | }) |
| | | .call(drag(simulation)) |
| | | |
| | |
| | | .attr("dy", (d) => nodeRadius(d) + 8 + "px") |
| | | .attr("text-anchor", "middle") |
| | | .text((d) => content[d.id]?.title || d.id.replace("-", " ")) |
| | | .style("opacity", 0) |
| | | .style('opacity', (opacityScale - 1) / 3.75) |
| | | .style("pointer-events", "none") |
| | | .style("font-size", "0.4em") |
| | | .style('font-size', fontSize+'em') |
| | | .raise() |
| | | .call(drag(simulation)) |
| | | |
| | |
| | | .on("zoom", ({ transform }) => { |
| | | link.attr("transform", transform) |
| | | node.attr("transform", transform) |
| | | const scale = transform.k |
| | | const scale = transform.k * opacityScale; |
| | | const scaledOpacity = Math.max((scale - 1) / 3.75, 0) |
| | | labels.attr("transform", transform).style("opacity", scaledOpacity) |
| | | }), |