A demonstration of d3-sankey using Zoom control, drag nodes (vertically), tooltip boxs, colors links (by color's nodes) and nodes values.
Thanks Mike Bostock!
| border: no | |
| license: gpl-3.0 |
| function d3sankey() { | |
| var sankey = {}, | |
| nodeWidth = 20, | |
| nodePadding = 8, | |
| size = [1, 1], | |
| nodes = [], | |
| links = []; | |
| sankey.nodeWidth = function(_) { | |
| if (!arguments.length) return nodeWidth; | |
| nodeWidth = +_; | |
| return sankey; | |
| }; | |
| sankey.nodePadding = function(_) { | |
| if (!arguments.length) return nodePadding; | |
| nodePadding = +_; | |
| return sankey; | |
| }; | |
| sankey.nodes = function(_) { | |
| if (!arguments.length) return nodes; | |
| nodes = _; | |
| return sankey; | |
| }; | |
| sankey.links = function(_) { | |
| if (!arguments.length) return links; | |
| links = _; | |
| return sankey; | |
| }; | |
| sankey.size = function(_) { | |
| if (!arguments.length) return size; | |
| size = _; | |
| return sankey; | |
| }; | |
| sankey.layout = function(iterations) { | |
| computeNodeLinks(); | |
| computeNodeValues(); | |
| computeNodeBreadths(); | |
| computeNodeDepths(iterations); | |
| computeLinkDepths(); | |
| return sankey; | |
| }; | |
| sankey.relayout = function() { | |
| computeLinkDepths(); | |
| return sankey; | |
| }; | |
| sankey.link = function() { | |
| var curvature = .5; | |
| function link(d) { | |
| var x0 = d.source.x + d.source.dx, | |
| x1 = d.target.x, | |
| xi = d3.interpolateNumber(x0, x1), | |
| x2 = xi(curvature), | |
| x3 = xi(1 - curvature), | |
| y0 = d.source.y + d.sy + d.dy / 2, | |
| y1 = d.target.y + d.ty + d.dy / 2; | |
| return "M" + x0 + "," + y0 + "C" + x2 + "," + y0 + " " + x3 + "," + y1 + " " + x1 + "," + y1; | |
| } | |
| link.curvature = function(_) { | |
| if (!arguments.length) return curvature; | |
| curvature = +_; | |
| return link; | |
| }; | |
| return link; | |
| }; | |
| // Populate the sourceLinks and targetLinks for each node. | |
| // Also, if the source and target are not objects, assume they are indices. | |
| function computeNodeLinks() { | |
| nodes.forEach(function(node) { | |
| node.sourceLinks = []; | |
| node.targetLinks = []; | |
| }); | |
| links.forEach(function(link) { | |
| var source = link.source, | |
| target = link.target; | |
| if (typeof source === "number") source = link.source = nodes[link.source]; | |
| if (typeof target === "number") target = link.target = nodes[link.target]; | |
| source.sourceLinks.push(link); | |
| target.targetLinks.push(link); | |
| }); | |
| } | |
| // Compute the value (size) of each node by summing the associated links. | |
| function computeNodeValues() { | |
| nodes.forEach(function(node) { | |
| node.value = Math.max( | |
| d3.sum(node.sourceLinks, value), | |
| d3.sum(node.targetLinks, value)); | |
| }); | |
| } | |
| // Iteratively assign the breadth (x-position) for each node. | |
| // Nodes are assigned the maximum breadth of incoming neighbors plus one; | |
| // nodes with no incoming links are assigned breadth zero, while | |
| // nodes with no outgoing links are assigned the maximum breadth. | |
| function computeNodeBreadths() { | |
| var remainingNodes = nodes, | |
| nextNodes, | |
| x = 0; | |
| while (remainingNodes.length) { | |
| nextNodes = []; | |
| remainingNodes.forEach(function(node) { | |
| node.x = x; | |
| node.dx = nodeWidth; | |
| node.sourceLinks.forEach(function(link) { | |
| nextNodes.push(link.target); | |
| }); | |
| }); | |
| remainingNodes = nextNodes; | |
| ++x; | |
| } | |
| // | |
| moveSinksRight(x); | |
| scaleNodeBreadths((size[0] - nodeWidth) / (x - 1)); | |
| } | |
| function moveSourcesRight() { | |
| nodes.forEach(function(node) { | |
| if (!node.targetLinks.length) { | |
| node.x = d3.min(node.sourceLinks, function(d) { | |
| return d.target.x; | |
| }) - 1; | |
| } | |
| }); | |
| } | |
| function moveSinksRight(x) { | |
| nodes.forEach(function(node) { | |
| if (!node.sourceLinks.length) { | |
| node.x = x - 1; | |
| } | |
| }); | |
| } | |
| function scaleNodeBreadths(kx) { | |
| nodes.forEach(function(node) { | |
| node.x *= kx; | |
| }); | |
| } | |
| function computeNodeDepths(iterations) { | |
| var nodesByBreadth = d3.nest() | |
| .key(function(d) { | |
| return d.x; | |
| }) | |
| .sortKeys(d3.ascending) | |
| .entries(nodes) | |
| .map(function(d) { | |
| return d.values; | |
| }); | |
| // | |
| initializeNodeDepth(); | |
| resolveCollisions(); | |
| for (var alpha = 1; iterations > 0; --iterations) { | |
| relaxRightToLeft(alpha *= .99); | |
| resolveCollisions(); | |
| relaxLeftToRight(alpha); | |
| resolveCollisions(); | |
| } | |
| function initializeNodeDepth() { | |
| var ky = d3.min(nodesByBreadth, function(nodes) { | |
| return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value); | |
| }); | |
| nodesByBreadth.forEach(function(nodes) { | |
| nodes.forEach(function(node, i) { | |
| node.y = i; | |
| node.dy = node.value * ky; | |
| }); | |
| }); | |
| links.forEach(function(link) { | |
| link.dy = link.value * ky; | |
| }); | |
| } | |
| function relaxLeftToRight(alpha) { | |
| nodesByBreadth.forEach(function(nodes, breadth) { | |
| nodes.forEach(function(node) { | |
| if (node.targetLinks.length) { | |
| var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value); | |
| node.y += (y - center(node)) * alpha; | |
| } | |
| }); | |
| }); | |
| function weightedSource(link) { | |
| return center(link.source) * link.value; | |
| } | |
| } | |
| function relaxRightToLeft(alpha) { | |
| nodesByBreadth.slice().reverse().forEach(function(nodes) { | |
| nodes.forEach(function(node) { | |
| if (node.sourceLinks.length) { | |
| var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value); | |
| node.y += (y - center(node)) * alpha; | |
| } | |
| }); | |
| }); | |
| function weightedTarget(link) { | |
| return center(link.target) * link.value; | |
| } | |
| } | |
| function resolveCollisions() { | |
| nodesByBreadth.forEach(function(nodes) { | |
| var node, | |
| dy, | |
| y0 = 0, | |
| n = nodes.length, | |
| i; | |
| // Push any overlapping nodes down. | |
| nodes.sort(ascendingDepth); | |
| for (i = 0; i < n; ++i) { | |
| node = nodes[i]; | |
| dy = y0 - node.y; | |
| if (dy > 0) node.y += dy; | |
| y0 = node.y + node.dy + nodePadding; | |
| } | |
| // If the bottommost node goes outside the bounds, push it back up. | |
| dy = y0 - nodePadding - size[1]; | |
| if (dy > 0) { | |
| y0 = node.y -= dy; | |
| // Push any overlapping nodes back up. | |
| for (i = n - 2; i >= 0; --i) { | |
| node = nodes[i]; | |
| dy = node.y + node.dy + nodePadding - y0; | |
| if (dy > 0) node.y -= dy; | |
| y0 = node.y; | |
| } | |
| } | |
| }); | |
| } | |
| function ascendingDepth(a, b) { | |
| return a.y - b.y; | |
| } | |
| } | |
| function computeLinkDepths() { | |
| nodes.forEach(function(node) { | |
| node.sourceLinks.sort(ascendingTargetDepth); | |
| node.targetLinks.sort(ascendingSourceDepth); | |
| }); | |
| nodes.forEach(function(node) { | |
| var sy = 0, | |
| ty = 0; | |
| node.sourceLinks.forEach(function(link) { | |
| link.sy = sy; | |
| sy += link.dy; | |
| }); | |
| node.targetLinks.forEach(function(link) { | |
| link.ty = ty; | |
| ty += link.dy; | |
| }); | |
| }); | |
| function ascendingSourceDepth(a, b) { | |
| return a.source.y - b.source.y; | |
| } | |
| function ascendingTargetDepth(a, b) { | |
| return a.target.y - b.target.y; | |
| } | |
| } | |
| function center(node) { | |
| return node.y + node.dy / 2; | |
| } | |
| function value(link) { | |
| return link.value; | |
| } | |
| return sankey; | |
| }; |
| <!DOCTYPE html> | |
| <html> | |
| <meta charset="utf-8"/> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | |
| <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet" id="bootstrap-css"> | |
| <script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script> | |
| <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" id="bootstrap-css"> | |
| <script src="http://d3js.org/d3.v3.js"></script> | |
| <script type="text/javascript" src="https://cdn.rawgit.com/Caged/d3-tip/896d387c653b4d73cea9cdd0740aa8794754417a/index.js"></script> | |
| <!-- Get inicial sample from http://jsfiddle.net/QwMPS/ --> | |
| <script src="d3sankey.js"></script> | |
| <style type="text/css"> | |
| svg text { | |
| font-size: 14px; | |
| stroke-width: 0; | |
| fill: black; | |
| } | |
| html, body { | |
| font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Open Sans","Helvetica Neue",sans-serif; | |
| justify-content: center; | |
| align-items: center; | |
| font-size: 12px; | |
| overflow: hidden; | |
| padding: 0; | |
| margin: 0; | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .sankeybox { | |
| display: flex; | |
| flex-direction: column; | |
| height: 100%; | |
| position: relative; | |
| } | |
| .sankey .node text { | |
| pointer-events: none; | |
| } | |
| .sankey .link { | |
| fill: none; | |
| stroke: #000; | |
| stroke-opacity: .16; | |
| transition-property: stroke-opacity; | |
| transition-duration: 0.5s; | |
| } | |
| .sankey .link:hover { | |
| stroke-opacity: .5; | |
| } | |
| h1, h2, h3 { | |
| line-height: 12px !important; | |
| } | |
| .d3-tip h1 { | |
| font-size: 14px; | |
| padding: 0; | |
| margin-bottom: 5px; | |
| width: 100%; | |
| } | |
| .d3-tip h2 { | |
| font-weight: bold; | |
| font-size: 12px; | |
| padding-right: inherit; | |
| padding-left: inherit; | |
| padding-top: 2px; | |
| padding-bottom: 2px; | |
| margin: 0px; | |
| } | |
| .d3-tip h3 { | |
| font-weight: normal; | |
| font-size: 8px; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| .d3-tip table { | |
| font-weight: normal; | |
| font-size: 12px; | |
| padding: none; | |
| margin: 0; | |
| width: 100%; | |
| border: none; | |
| border-collapse: collapse; | |
| } | |
| .d3-tip td { | |
| padding-top: 2px; | |
| padding-bottom: 2px; | |
| } | |
| .d3-tip .col-left { | |
| padding-right: 8px; | |
| } | |
| .d3-tip .table-wrapper { | |
| margin: 0; | |
| padding: inherit; | |
| border: none; | |
| } | |
| .d3-tip { | |
| line-height: 1; | |
| font-weight: normal; | |
| padding: 4px; | |
| background: white; | |
| color: black; | |
| border-radius: 2px; | |
| pointer-events: none; | |
| background: white; | |
| box-shadow: 1px 1px 4px grey; | |
| } | |
| </style> | |
| <style> | |
| .node rect { | |
| cursor: move; | |
| fill-opacity: .9; | |
| shape-rendering: crispEdges; | |
| } | |
| .node text { | |
| pointer-events: none; | |
| text-shadow: 0px 0px 13px #fff; | |
| } | |
| .node:hover { | |
| stroke-opacity: .2; | |
| } | |
| .link { | |
| fill: none; | |
| stroke: #000; | |
| stroke-opacity: .16; | |
| transition-property: stroke-opacity; | |
| transition-duration: 0.5s; | |
| } | |
| .link:hover { | |
| stroke-opacity: .5; | |
| } | |
| </style> | |
| <style> | |
| .d3-zoom-controls { | |
| position: absolute; | |
| left: 15px; | |
| top: 10px; | |
| } | |
| .d3-zoom-controls .btn-circle { | |
| width: 30px; | |
| height: 30px; | |
| align-items: center; | |
| padding: 0px 0; | |
| font-size: 18px; | |
| line-height: 2.00; | |
| display: grid; | |
| border-radius: 30px; | |
| background: rgba(255, 255, 255, 0.7); | |
| color: gray; | |
| margin-top: 10px; | |
| } | |
| </style> | |
| <body> | |
| <div id="sankey" class="sankeybox"> | |
| <div class="d3-zoom-controls"> | |
| <a data-zoom="+0.5" class="btn btn-circle"><span class="fa fa-plus"></span></a> | |
| <a data-zoom="-0.5" class="btn btn-circle"><span class="fa fa-minus"></span></a> | |
| <a data-zoom="0" class="btn btn-circle"><span class="fa fa-crosshairs"></span></a> | |
| </div> | |
| </div> | |
| <script> | |
| var zoom = {}; | |
| var drag = {}; | |
| var svg = {}; | |
| //Zoom function: | |
| //Programmatic Pan+Zoom III (Mike Bostock’s Block 7ec977c95910dd026812) | |
| //https://bl.ocks.org/mbostock/7ec977c95910dd026812 | |
| d3.selectAll("a[data-zoom]").on("click", clicked); | |
| function clicked() { | |
| var valueZoom = this.getAttribute("data-zoom"); | |
| if (valueZoom != 0) | |
| { | |
| svg.call(zoom); | |
| // Record the coordinates (in data space) of the center (in screen space). | |
| var center0 = zoom.center(), translate0 = zoom.translate(), coordinates0 = coordinates(center0); | |
| zoom.scale(zoom.scale() * Math.pow(2, +valueZoom)); | |
| // Translate back to the center. | |
| var center1 = point(coordinates0); | |
| zoom.translate([translate0[0] + center0[0] - center1[0], translate0[1] + center0[1] - center1[1]]); | |
| svg.transition().duration(750).call(zoom.event); | |
| } else { | |
| fitZoom(); | |
| } | |
| } | |
| function fitZoom() | |
| { | |
| svg.transition().duration(500).call(zoom.translate([20,10]).scale(1).event); | |
| } | |
| function coordinates(point) { | |
| var scale = zoom.scale(), translate = zoom.translate(); | |
| return [(point[0] - translate[0]) / scale, (point[1] - translate[1]) / scale]; | |
| } | |
| function point(coordinates) { | |
| var scale = zoom.scale(), translate = zoom.translate(); | |
| return [coordinates[0] * scale + translate[0], coordinates[1] * scale + translate[1]]; | |
| } | |
| function showSankey(energy) | |
| { | |
| $('.d3-tip-nodes').remove(); //clear olds tips | |
| var chartBox = d3.select("#sankey").node().getBoundingClientRect(); | |
| var margin = {top: 10, right: 10, bottom: 10, left: 20}, | |
| width = chartBox.width - chartBox.left - margin.left - margin.right, | |
| height = chartBox.height - chartBox.top - margin.top - margin.bottom; | |
| var linkTooltipOffset = 72, | |
| nodeTooltipOffset = 130; | |
| //Tooltip function: | |
| //D3 sankey diagram with view options (Austin Czarnecki’s Block cc6371af0b726e61b9ab) | |
| //https://bl.ocks.org/austinczarnecki/cc6371af0b726e61b9ab | |
| var tipLinks = d3.tip() | |
| .attr('class', 'd3-tip') | |
| .offset([-10,0]); | |
| var tipNodes = d3.tip() | |
| .attr('class', 'd3-tip d3-tip-nodes') | |
| .offset([-10, 0]); | |
| function formatAmount(val) { | |
| //return val.toLocaleString("en-US", {style: 'currency', currency: "USD"}).replace(/\.[0-9]+/, ""); | |
| return parseFloat(val).toFixed(1) + " $"; | |
| }; | |
| // "➡" | |
| tipLinks.html(function(d) { | |
| var title, candidate; | |
| if (energy.links.indexOf(d.source.name) > -1) { | |
| candidate = d.source.name; | |
| title = d.target.name; | |
| } else { | |
| candidate = d.target.name; | |
| title = d.source.name; | |
| } | |
| var html = '<div class="table-wrapper">'+ | |
| '<h1>'+title+'</h1>'+ | |
| '<table>'+ | |
| '<tr>'+ | |
| '<td class="col-left">'+candidate+'</td>'+ | |
| '<td align="right">'+formatAmount(d.value)+'</td>'+ | |
| '</tr>'+ | |
| '</table>'+ | |
| '</div>'; | |
| return html; | |
| }); | |
| tipNodes.html(function(d) { | |
| var object = d3.entries(d), | |
| nodeName = object[1].value, | |
| linksTo = object[3].value, | |
| linksFrom = object[4].value, | |
| html; | |
| html = '<div class="table-wrapper">'+ | |
| '<h1>'+nodeName+'</h1>'+ | |
| '<table>'; | |
| if (linksFrom.length > 0 & linksTo.length > 0) { | |
| html+= '<tr><td><h2>Input:</h2></td><td></td></tr>' | |
| } | |
| for (i = 0; i < linksFrom.length; ++i) { | |
| html += '<tr>'+ | |
| '<td class="col-left">'+linksFrom[i].source.name+'</td>'+ | |
| '<td align="right">'+formatAmount(linksFrom[i].value)+'</td>'+ | |
| '</tr>'; | |
| } | |
| if (linksFrom.length > 0 & linksTo.length > 0) { | |
| html+= '<tr><td><h2>Output:</h2></td><td></td></tr>' | |
| } | |
| for (i = 0; i < linksTo.length; ++i) { | |
| html += '<tr>'+ | |
| '<td class="col-left">'+linksTo[i].target.name+'</td>'+ | |
| '<td align="right">'+formatAmount(linksTo[i].value)+'</td>'+ | |
| '</tr>'; | |
| } | |
| html += '</table></div>'; | |
| return html; | |
| }); | |
| function zoomed() { | |
| svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); | |
| } | |
| function dragstarted(d) { | |
| d3.select(this).classed("dragging", true); | |
| } | |
| function dragged(d) { | |
| d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y); | |
| } | |
| function dragended(d) { | |
| d3.select(this).classed("dragging", false); | |
| } | |
| drag = d3.behavior.drag() | |
| .origin(function(d) { return d; }) | |
| .on("dragstart", dragstarted) | |
| .on("drag", dragged) | |
| .on("dragend", dragended); | |
| zoom = d3.behavior.zoom() | |
| .scaleExtent([0, 5]) | |
| .center([width / 2, height / 2]) | |
| .on("zoom", zoomed); | |
| var formatNumber = d3.format(",.0f"), | |
| format = function(d) { return formatNumber(d) + " $"; }, | |
| color = d3.scale.category20(); | |
| svg = d3.select("#sankey").append("svg") | |
| .attr("width", width + chartBox.left + margin.left + margin.right) | |
| .attr("height", height + chartBox.top + margin.top + margin.bottom) | |
| .call(zoom) | |
| .call(tipLinks) | |
| .call(tipNodes) | |
| .append("g") | |
| .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
| var sankey = d3sankey() | |
| .nodeWidth(40) | |
| .nodePadding(15) | |
| .size([width, height]); | |
| var path = sankey.link(); | |
| sankey | |
| .nodes(energy.nodes) | |
| .links(energy.links) | |
| .layout(32); | |
| var fontScale = d3.scale.linear().domain(d3.extent(energy.nodes, function(d) { return d.value })).range([18, 30]); | |
| var link = svg.append("g").selectAll(".link") | |
| .data(energy.links) | |
| .enter().append("path") | |
| .attr("class", "link") | |
| .attr("d", path) | |
| .style("stroke", function(d){ return d.source.color; }) | |
| .style("stroke-width", function(d) { return Math.max(1, d.dy); }) | |
| .sort(function(a, b) { return b.dy - a.dy; }) | |
| .on('mousemove', function(event) { | |
| tipLinks | |
| .style("top", (d3.event.pageY - linkTooltipOffset) + "px") | |
| .style("left", function () { | |
| var left = (Math.max(d3.event.pageX - linkTooltipOffset, 10)); | |
| left = Math.min(left, window.innerWidth - $('.d3-tip').width() - 20) | |
| return left + "px"; }) | |
| }) | |
| .on('mouseover', tipLinks.show) | |
| .on('mouseout', tipLinks.hide); | |
| var node = svg.append("g").selectAll(".node") | |
| .data(energy.nodes) | |
| .enter().append("g") | |
| .attr("class", "node") | |
| .attr("transform", function(d) { | |
| return "translate(" + d.x + "," + d.y + ")"; | |
| }) | |
| .on('mousemove', function(event) { | |
| tipNodes | |
| .style("top", (d3.event.pageY - $('.d3-tip-nodes').height() - 20) + "px") | |
| .style("left", function () { | |
| var left = (Math.max(d3.event.pageX - nodeTooltipOffset, 10)); | |
| left = Math.min(left, window.innerWidth - $('.d3-tip').width() - 20) | |
| return left + "px"; }) | |
| }) | |
| .on('mouseover', tipNodes.show) | |
| .on('mouseout', tipNodes.hide) | |
| .call(d3.behavior.drag() | |
| .origin(function(d) { return d; }) | |
| .on("dragstart", function() { | |
| d3.event.sourceEvent.stopPropagation(); //Disable drag sankey on node select | |
| this.parentNode.appendChild(this); }) | |
| .on("drag", dragmove)); | |
| node.append("rect") | |
| .attr("height", function(d) { return d.dy; }) | |
| .attr("width", sankey.nodeWidth()) | |
| .style("fill", function(d) { | |
| if (d.color == undefined) | |
| return d.color = color(d.name.replace(/ .*/, "")); //get new color if node.color is null | |
| return d.color; | |
| }) | |
| .style("stroke", function(d) { return d3.rgb(d.color).darker(2); }); | |
| node.append("text") | |
| .attr("class","nodeValue") | |
| .text(function(d) { return d.name + "\n" + format(d.value); }); | |
| node.selectAll("text.nodeValue") | |
| .attr("x", sankey.nodeWidth() / 2) | |
| .attr("y", function (d) { return (d.dy / 2) }) | |
| .text(function (d) { return formatNumber(d.value); }) | |
| .attr("dy", 5) | |
| .attr("text-anchor", "middle"); | |
| node.append("text") | |
| .attr("class","nodeLabel") | |
| .style("fill", function(d) { | |
| return d3.rgb(d.color).darker(2.4); | |
| }) | |
| .style("font-size", function(d) { | |
| return fontScale(d.value) + "px"; | |
| }); | |
| node.selectAll("text.nodeLabel") | |
| .attr("x", -6) | |
| .attr("y", function(d) { return d.dy / 2; }) | |
| .attr("dy", ".35em") | |
| .attr("text-anchor", "end") | |
| .attr("transform", null) | |
| .text(function(d) { return d.name; }) | |
| .filter(function(d) { return d.x < width / 2; }) | |
| .attr("x", 6 + sankey.nodeWidth()) | |
| .attr("text-anchor", "start"); | |
| function dragmove(d) { | |
| d3.select(this) | |
| .attr("transform", "translate(" + d.x + "," + | |
| (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")"); | |
| sankey.relayout(); | |
| link.attr("d", path); | |
| }; | |
| fitZoom(); | |
| } | |
| function getData() { | |
| return { | |
| "nodes": [{ | |
| "node": 0, | |
| "name": "node0", | |
| color: "#1f77b4" | |
| }, { | |
| "node": 1, | |
| "name": "node1", | |
| color:"#aec7e8" | |
| }, { | |
| "node": 2, | |
| "name": "node2", | |
| color: "#ff7f0e" | |
| }, { | |
| "node": 3, | |
| "name": "node3", | |
| color: "#ffbb78" | |
| }, { | |
| "node": 4, | |
| "name": "node4", | |
| color: "#2ca02c" | |
| }, { | |
| "node": 5, | |
| "name": "node5", | |
| color: "#98df8a" | |
| }, { | |
| "node": 6, | |
| "name": "node6", | |
| color: "#d62728" | |
| }, { | |
| "node": 7, | |
| "name": "node7", | |
| color: "#ff9896" | |
| }], | |
| "links": [{ | |
| "source": 0, | |
| "target": 2, | |
| "value": 25 | |
| }, { | |
| "source": 1, | |
| "target": 2, | |
| "value": 5 | |
| }, { | |
| "source": 1, | |
| "target": 3, | |
| "value": 20 | |
| }, { | |
| "source": 2, | |
| "target": 4, | |
| "value": 29 | |
| }, { | |
| "source": 2, | |
| "target": 5, | |
| "value": 1 | |
| }, { | |
| "source": 3, | |
| "target": 4, | |
| "value": 10 | |
| }, { | |
| "source": 3, | |
| "target": 5, | |
| "value": 2 | |
| }, { | |
| "source": 3, | |
| "target": 6, | |
| "value": 8 | |
| }, { | |
| "source": 4, | |
| "target": 7, | |
| "value": 39 | |
| }, { | |
| "source": 5, | |
| "target": 7, | |
| "value": 3 | |
| }, { | |
| "source": 6, | |
| "target": 7, | |
| "value": 8 | |
| }]}; | |
| }; | |
| showSankey(getData()); | |
| </script> | |
| </body> | |
| </html> |
Cool Stuff! Could you please change the license to Apache 2.0 so that your work is widely used.
A demonstration of d3-sankey using Zoom control, drag nodes (vertically), tooltip boxs, colors links (by color's nodes) and nodes values.
Thanks Mike Bostock!