d3 / d3

Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:

Home Page:https://d3js.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Zoom and click on d3 Sankey

gitgitwhat opened this issue · comments

I'm having trouble making zoom/pan work with sankey along with mouse events.

There are plenty of examples on the ether that show how to do zooming but they all seem to be using old d3 versions. Nothing that I've tried has worked. Same with node double clicks.

Here is my code based on https://observablehq.com/@d3/sankey-component.

	const margin = {
		top: 20,
		right: 20,
		bottom: 20,
		left: 20,
	};

	var container = d3.select("#" + elem);
	const svg = container.append("svg")
		.attr("width", width)
		.attr("height", height)
		.attr("viewBox", [0, 0, width, height])
		.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");

	const sankey = d3.sankey()
		.nodeId(d => d.name)
		.nodeAlign(d3[d3.sankeyLeft])
		.nodeWidth(20)
		.nodePadding(20)
		.extent([[1, 5], [width - 1, height - 5]]);

	const { nodes, links } = sankey({
		nodes: na.map(d => Object.assign({}, d)),
		links: la.map(d => Object.assign({}, d))
	});

	const color = d3.scaleOrdinal(d3.schemeCategory10);
	const vsclr = function(d) {
		return color(d.category);
	};

	const rect = svg.append("g")
		.attr('transform', `translate(${margin.left}, ${margin.top})`)
		.attr("stroke", "#000")
		.selectAll()
		.data(nodes)
		.on("dblclick", d => {
			console.log(d);
		})
		.join("rect")
		.attr("x", d => d.x0)
		.attr("y", d => d.y0)
		.attr("height", d => d.y1 - d.y0)
		.attr("width", d => d.x1 - d.x0)
		.attr("fill", d => vsclr(d));

	const link = svg.append("g")
		.attr("fill", "none")
		.attr("stroke-opacity", 0.5)
		.selectAll()
		.data(links)
		.join("g")
		.style("mix-blend-mode", "multiply");

	var linkColor = "target";
	var uid = 1;

	if (linkColor === "source-target") {
		const gradient = link.append("linearGradient")
			.attr("id", d => (d.uid = (uid++).toString()).id)
			.attr("gradientUnits", "userSpaceOnUse")
			.attr("x1", d => d.source.x1)
			.attr("x2", d => d.target.x0);
		gradient.append("stop")
			.attr("offset", "0%")
			.attr("stop-color", d => vsclr(d.source));
		gradient.append("stop")
			.attr("offset", "100%")
			.attr("stop-color", d => vsclr(d.target));
	}

	link.append("path")
		.attr("d", d3.sankeyLinkHorizontal())
		.attr("stroke", linkColor === "source-target" ? (d) => d.uid
			: linkColor === "source" ? (d) => vsclr(d.source)
				: linkColor === "target" ? (d) => vsclr(d.target)
					: linkColor)
		.attr("stroke-width", d => Math.max(1, d.width));

	const tooltip = container
		.append('div')
		.attr('id', 'tooltip')
		.style('position', 'absolute')
		.style('padding', '1rem')
		.style('background-color', 'white');

	link.on("mouseover", (event, d) => {
		tooltip.style("opacity", 1)
			.style("left", (event.offsetX + 3) + "px")
			.style("top", (event.offsetY +  5) + "px")
			.html(`<strong>${d.source.name}</strong> - <strong>${d.target.name}</strong>`);
	})
	.on("mouseout", (event, d) => {
		tooltip.style("opacity", 0)
	});

	svg.append("g")
		.selectAll()
		.data(nodes)
		.join("text")
		.attr("x", d => d.x0 < width / 2 ? d.x1 + 6 : d.x0 - 6)
		.attr("y", d => (d.y1 + d.y0) / 2)
		.attr("dy", "0.35em")
		.attr("text-anchor", d => d.x0 < width / 2 ? "start" : "end")
		.text(d => d.name);

	var zoom = d3.zoom()
		.scaleExtent([1, 8])
		.on('zoom', function(event) {
			rect.selectAll().attr('transform', event.transform);
	});

	svg.call(zoom);