var margin = { top: 150, right: 30, bottom: 30, left: 30 } var width = 1200 var height = 600 var sankey = d3.sankeyCircular() .nodeWidth(10) .nodePadding(5) .size([width, height]) .nodeId(function (d) { return d.name }) .nodeAlign(d3.sankeyLeft) .iterations(32) .circularLinkGap(2) var svg = d3 .select('#chart') .append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) var g = svg .append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') var linkG = g .append('g') .attr('class', 'links') .attr('fill', 'none') // .attr("stroke-opacity", 0.3) .selectAll('path') var nodeG = g .append('g') .attr('class', 'nodes') .attr('font-family', 'sans-serif') .attr('font-size', 10) .selectAll('g') // run the Sankey + circular over the data let sankeyData = sankey(data) let sankeyNodes = sankeyData.nodes let sankeyLinks = sankeyData.links var node = nodeG.data(sankeyNodes).enter().append('g') node .append('rect') .attr('x', function (d) { return d.x0 }) .attr('y', function (d) { return d.y0 }) .attr('height', function (d) { return d.y1 - d.y0 }) .attr('width', function (d) { return d.x1 - d.x0 }) .style('fill', "black") .style('opacity', 0.5) .on('mouseover', function (d) { let thisName = d.name node.selectAll('rect').style('opacity', function (d) { return highlightNodes(d, thisName) }) d3.selectAll('.sankey-link').style('opacity', function (l) { return l.source.name == thisName || l.target.name == thisName ? 0.8 : 0.1 }) node.selectAll('text').style('opacity', function (d) { return highlightNodes(d, thisName) }) }) .on('mouseout', function (d) { d3.selectAll('rect').style('opacity', 0.5) d3.selectAll('.sankey-link').style('opacity', 0.3) d3.selectAll('text').style('opacity', 1) }) node .append('text') .attr('x', function (d) { return (d.x0 + d.x1) / 2 }) .attr('y', function (d) { return d.y0 - 12 }) .attr('dy', '0.35em') .attr('text-anchor', 'middle') .text(function (d) { return d.name }) node.append('title').text(function (d) { return d.name + '\n' + d.value }) var link = linkG.data(sankeyLinks).enter().append('g') link .append('path') .attr('class', 'sankey-link') .attr('d', function (link) { return link.path }) .style('stroke-width', function (d) { return Math.max(1, d.width) }) .style('opacity', 0.3) .style('stroke', function (link, i) { return link.circular ? 'red' : 'black' }) link.append('title').text(function (d) { return d.source.name + ' → ' + d.target.name + '\n Index: ' + d.index }) let arrows = pathArrows() .arrowLength(10) .gapLength(75) .arrowHeadSize(3) .path(function (link) { return link.path }) var arrowsG = linkG .data(sankeyLinks) .enter() .filter(function (link) { return link.value > 10 }) .append('g') .attr('class', 'g-arrow') .call(arrows) function highlightNodes (node, name) { let opacity = 0.3 if (node.name == name) { opacity = 0.9 } node.sourceLinks.forEach(function (link) { if (link.target.name == name) { opacity = 0.9 } }) node.targetLinks.forEach(function (link) { if (link.source.name == name) { opacity = 0.9 } }) return opacity }