简体   繁体   English

D3.js Sankey粒子图

[英]D3.js Sankey diagram with particles

I've been experimenting with sankey diagrams using the d3 Javascript library. 我一直在尝试使用d3 Javascript库测试sankey图。 I've recently come across a very nice sankey diagram on bl.ocks. 我最近在bl.ocks上遇到了一个非常漂亮的sankey图。

https://bl.ocks.org/micahstubbs/ae0946f9efe18fc4f67460e7387abc0b 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). 在将其应用于自己的sankey图之前,我要做的第一件事是复制bl.ocks页面上的所有代码,并在自己的代码编辑器(Dreamweaver)中进行尝试。 The problem is whenever I try to load the sankey I get the following console error: 问题是,每当我尝试加载sankey时,都会出现以下控制台错误:

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. 我不太了解这里发生的情况,因为在bl.ocks页面上似乎工作正常。 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. 它在vis.js代码的底部。 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. 来自MDN:常量是程序在正常执行期间无法更改的值。 It cannot change through re-assignment, and it can't be redeclared. 它不能通过重新分配进行更改,也不能重新声明。 In JavaScript, constants are declared using the const keyword. 在JavaScript中,使用const关键字声明常量。 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. 但是我将下面的bl.ocks.org中的图表代码组合在一起。 It works on codepen too. 它也适用于Codepen。

 <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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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