简体   繁体   中英

D3.js Sankey diagram with particles

I've been experimenting with sankey diagrams using the d3 Javascript library. I've recently come across a very nice sankey diagram on bl.ocks.

https://bl.ocks.org/micahstubbs/ae0946f9efe18fc4f67460e7387abc0b

The first thing I tried before applying it to my own sankey diagram was copying all the code on the bl.ocks page and trying it out in my own code editor (Dreamweaver). The problem is whenever I try to load the sankey I get the following console error:

SyntaxError: missing = in const declaration

I don't really understand what's going on here since it seems to work just fine on the bl.ocks page. The piece of code where supposedly should be is the following:

for (const x in particles) {
  if ({}.hasOwnProperty.call(particles, x)) {
    const currentTime = elapsed - particles[x].time;
    // var currentPercent = currentTime / 1000 * particles[x].path.getTotalLength();
    particles[x].current = currentTime * 0.15 * particles[x].speed;
    const currentPos = particles[x].path.getPointAtLength(particles[x].current);
    context.beginPath();
    context.fillStyle = particles[x].link.particleColor(0);
    context.arc(currentPos.x, currentPos.y + particles[x].offset, particles[x].link.particleSize, 0, 2 * Math.PI);
    context.fill();
  }
}

It's at the bottom of the vis.js code. Could anyone help me out with getting this code to work? I love the visual effect and would like to learn to use it. Thanks you.

From MDN: A constant is a value that cannot be altered by the program during normal execution. It cannot change through re-assignment, and it can't be redeclared. In JavaScript, constants are declared using the const keyword. An initializer for a constant is required; that is, you must specify its value in the same statement in which it's declared (which makes sense, given that it can't be changed later).

It is possible that you data (energy) is not loaded correctly. I cannot tell you what is wrong unless you post the whole code. But I have combined the code of the diagram from the bl.ocks.org below. It works on codepen too.

 <html lang='en'> <head> <meta charset='utf-8' /> <title>Sankey Particles</title> <style> .node rect { cursor: move; fill-opacity: .9; shape-rendering: crispEdges; } .node text { pointer-events: none; text-shadow: 0 1px 0 #fff; } .link { fill: none; stroke: #000; stroke-opacity: .15; } .link:hover { stroke-opacity: .25; } svg { position: absolute; } canvas { position: absolute; } </style> </head> <body> <canvas width='960' height='960'></canvas> <svg width='960' height='960'></svg> <script src='https://cdnjs.cloudflare.com/ajax/libs/d3/4.8.0/d3.js' charset='utf-8' type='text/javascript'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/d3-sankey/0.4.2/d3-sankey.js' charset='utf-8' type='text/javascript'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/d3-timer/1.0.5/d3-timer.js' charset='utf-8' type='text/javascript'></script> <script src='vis.js'></script> <script> const margin = { top: 1, right: 1, bottom: 6, left: 1 }; const width = 960 - margin.left - margin.right; const height = 500 - margin.top - margin.bottom; const formatNumber = d3.format(",.0f"); const format = d => `${formatNumber(d)} TWh`; const color = d3.scaleOrdinal(d3.schemeCategory20); const svg = d3 .select("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left},${margin.top})`); const sankey = d3.sankey().nodeWidth(15).nodePadding(10).size([width, height]); const path = sankey.link(); const freqCounter = 1; var energy = { nodes: [ { name: "Agricultural 'waste'" }, { name: "Bio-conversion" }, { name: "Liquid" }, { name: "Losses" }, { name: "Solid" }, { name: "Gas" }, { name: "Biofuel imports" }, { name: "Biomass imports" }, { name: "Coal imports" }, { name: "Coal" }, { name: "Coal reserves" }, { name: "District heating" }, { name: "Industry" }, { name: "Heating and cooling - commercial" }, { name: "Heating and cooling - homes" }, { name: "Electricity grid" }, { name: "Over generation / exports" }, { name: "H2 conversion" }, { name: "Road transport" }, { name: "Agriculture" }, { name: "Rail transport" }, { name: "Lighting & appliances - commercial" }, { name: "Lighting & appliances - homes" }, { name: "Gas imports" }, { name: "Ngas" }, { name: "Gas reserves" }, { name: "Thermal generation" }, { name: "Geothermal" }, { name: "H2" }, { name: "Hydro" }, { name: "International shipping" }, { name: "Domestic aviation" }, { name: "International aviation" }, { name: "National navigation" }, { name: "Marine algae" }, { name: "Nuclear" }, { name: "Oil imports" }, { name: "Oil" }, { name: "Oil reserves" }, { name: "Other waste" }, { name: "Pumped heat" }, { name: "Solar PV" }, { name: "Solar Thermal" }, { name: "Solar" }, { name: "Tidal" }, { name: "UK land based bioenergy" }, { name: "Wave" }, { name: "Wind" } ], links: [ { source: 0, target: 1, value: 124.729 }, { source: 1, target: 2, value: 0.597 }, { source: 1, target: 3, value: 26.862 }, { source: 1, target: 4, value: 280.322 }, { source: 1, target: 5, value: 81.144 }, { source: 6, target: 2, value: 35 }, { source: 7, target: 4, value: 35 }, { source: 8, target: 9, value: 11.606 }, { source: 10, target: 9, value: 63.965 }, { source: 9, target: 4, value: 75.571 }, { source: 11, target: 12, value: 10.639 }, { source: 11, target: 13, value: 22.505 }, { source: 11, target: 14, value: 46.184 }, { source: 15, target: 16, value: 104.453 }, { source: 15, target: 14, value: 113.726 }, { source: 15, target: 17, value: 27.14 }, { source: 15, target: 12, value: 342.165 }, { source: 15, target: 18, value: 37.797 }, { source: 15, target: 19, value: 4.412 }, { source: 15, target: 13, value: 40.858 }, { source: 15, target: 3, value: 56.691 }, { source: 15, target: 20, value: 7.863 }, { source: 15, target: 21, value: 90.008 }, { source: 15, target: 22, value: 93.494 }, { source: 23, target: 24, value: 40.719 }, { source: 25, target: 24, value: 82.233 }, { source: 5, target: 13, value: 0.129 }, { source: 5, target: 3, value: 1.401 }, { source: 5, target: 26, value: 151.891 }, { source: 5, target: 19, value: 2.096 }, { source: 5, target: 12, value: 48.58 }, { source: 27, target: 15, value: 7.013 }, { source: 17, target: 28, value: 20.897 }, { source: 17, target: 3, value: 6.242 }, { source: 28, target: 18, value: 20.897 }, { source: 29, target: 15, value: 6.995 }, { source: 2, target: 12, value: 121.066 }, { source: 2, target: 30, value: 128.69 }, { source: 2, target: 18, value: 135.835 }, { source: 2, target: 31, value: 14.458 }, { source: 2, target: 32, value: 206.267 }, { source: 2, target: 19, value: 3.64 }, { source: 2, target: 33, value: 33.218 }, { source: 2, target: 20, value: 4.413 }, { source: 34, target: 1, value: 4.375 }, { source: 24, target: 5, value: 122.952 }, { source: 35, target: 26, value: 839.978 }, { source: 36, target: 37, value: 504.287 }, { source: 38, target: 37, value: 107.703 }, { source: 37, target: 2, value: 611.99 }, { source: 39, target: 4, value: 56.587 }, { source: 39, target: 1, value: 77.81 }, { source: 40, target: 14, value: 193.026 }, { source: 40, target: 13, value: 70.672 }, { source: 41, target: 15, value: 59.901 }, { source: 42, target: 14, value: 19.263 }, { source: 43, target: 42, value: 19.263 }, { source: 43, target: 41, value: 59.901 }, { source: 4, target: 19, value: 0.882 }, { source: 4, target: 26, value: 400.12 }, { source: 4, target: 12, value: 46.477 }, { source: 26, target: 15, value: 525.531 }, { source: 26, target: 3, value: 787.129 }, { source: 26, target: 11, value: 79.329 }, { source: 44, target: 15, value: 9.452 }, { source: 45, target: 1, value: 182.01 }, { source: 46, target: 15, value: 19.013 }, { source: 47, target: 15, value: 289.366 } ] }; sankey.nodes(energy.nodes).links(energy.links).layout(32); const link = svg .append("g") .selectAll(".link") .data(energy.links) .enter() .append("path") .attr("class", "link") .attr("d", path) .style("stroke-width", d => Math.max(1, d.dy)) .sort((a, b) => b.dy - a.dy); link .append("title") .text(d => `${d.source.name} → ${d.target.name}\\n${format(d.value)}`); const node = svg .append("g") .selectAll(".node") .data(energy.nodes) .enter() .append("g") .attr("class", "node") .attr("transform", d => `translate(${dx},${dy})`) .call( d3 .drag() .subject(d => d) .on("start", function() { this.parentNode.appendChild(this); }) .on("drag", dragmove) ); node .append("rect") .attr("height", d => d.dy) .attr("width", sankey.nodeWidth()) .style("fill", d => { d.color = color(d.name.replace(/ .*/, "")); return d.color; }) .style("stroke", "none") .append("title") .text(d => `${d.name}\\n${format(d.value)}`); node .append("text") .attr("x", -6) .attr("y", d => d.dy / 2) .attr("dy", ".35em") .attr("text-anchor", "end") .attr("transform", null) .text(d => d.name) .filter(d => dx < width / 2) .attr("x", 6 + sankey.nodeWidth()) .attr("text-anchor", "start"); function dragmove(d) { d3 .select(this) .attr( "transform", `translate(${dx},${(dy = Math.max( 0, Math.min(height - d.dy, d3.event.y) ))})` ); sankey.relayout(); link.attr("d", path); } const linkExtent = d3.extent(energy.links, d => d.value); const frequencyScale = d3.scaleLinear().domain(linkExtent).range([0.05, 1]); const particleSize = d3.scaleLinear().domain(linkExtent).range([1, 5]); energy.links.forEach(link => { link.freq = frequencyScale(link.value); link.particleSize = 2; link.particleColor = d3 .scaleLinear() .domain([0, 1]) .range([link.source.color, link.target.color]); }); const t = d3.timer(tick, 1000); let particles = []; function tick(elapsed, time) { particles = particles.filter(d => d.current < d.path.getTotalLength()); d3.selectAll("path.link").each(function(d) { // if (d.freq < 1) { for (let x = 0; x < 2; x += 1) { const offset = (Math.random() - 0.5) * (d.dy - 4); if (Math.random() < d.freq) { const length = this.getTotalLength(); particles.push({ link: d, time: elapsed, offset, path: this, length, animateTime: length, speed: 0.5 + Math.random() }); } } // } /* else { for (var x = 0; x<d.freq; x++) { var offset = (Math.random() - .5) * d.dy; particles.push({link: d, time: elapsed, offset: offset, path: this}) } } */ }); particleEdgeCanvasPath(elapsed); } function particleEdgeCanvasPath(elapsed) { const context = d3.select("canvas").node().getContext("2d"); context.clearRect(0, 0, 1000, 1000); context.fillStyle = "gray"; context.lineWidth = "1px"; for (const x in particles) { if ({}.hasOwnProperty.call(particles, x)) { const currentTime = elapsed - particles[x].time; // var currentPercent = currentTime / 1000 * particles[x].path.getTotalLength(); particles[x].current = currentTime * 0.15 * particles[x].speed; const currentPos = particles[x].path.getPointAtLength( particles[x].current ); context.beginPath(); context.fillStyle = particles[x].link.particleColor(0); context.arc( currentPos.x, currentPos.y + particles[x].offset, particles[x].link.particleSize, 0, 2 * Math.PI ); context.fill(); } } } </script> </body> </html> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM