Built with blockbuilder.org
| <!DOCTYPE html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <script src="http://d3js.org/d3.v3.min.js"></script> | |
| <script src="http://duhoang.com/particles/js/underscore-min.js"></script> | |
| <script src="http://duhoang.com/particles/js/Tween.js"></script> | |
| <link type="text/css" href="http://duhoang.com/particles/css/style.css" rel="stylesheet"/> | |
| <style> | |
| body { | |
| margin: 0; | |
| min-width: 960px; | |
| } | |
| rect { | |
| fill: none; | |
| pointer-events: all; | |
| } | |
| circle { | |
| fill: none; | |
| stroke-width: 2.5px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <script> | |
| var width = Math.max(960, innerWidth), | |
| height = Math.max(500, innerHeight); | |
| var margin = {top: 0, left: 48, right: 48, bottom: 0}; | |
| var numParticles = 64; | |
| var numPointsPerPath = 12; | |
| var avgDist = (width + 240)/numPointsPerPath; | |
| var svg = d3.select("body").append("svg") | |
| .attr("width", width) | |
| .attr("height", height); | |
| svg.append("rect") | |
| .attr("width", width) | |
| .attr("height", height); | |
| var paths = []; | |
| for (i = 0; i < numParticles; i++) { | |
| var p = []; | |
| var rev = Math.random() * .8 + .2; | |
| var multiplier = Math.random() * 16 + 2; | |
| var yOffset = d3.scale.linear() | |
| .domain([0, numPointsPerPath/5, numPointsPerPath * .8,numPointsPerPath]) | |
| .range([32, 48, -24, 0]); | |
| var xStart = (Math.random() * 64); | |
| var yStart = (Math.random() * 64) + height/1.8; | |
| var waveFunc = function(val) { | |
| return i%2 === 0 ? Math.cos(val) : Math.sin(val); | |
| } | |
| for (j = 0; j < numPointsPerPath; j++) { | |
| var x = avgDist * j + margin.left + xStart; | |
| var y = waveFunc(j * rev) * (j * multiplier) + (j * yOffset(j)) + yStart; | |
| p.push([x,y]); | |
| } | |
| paths.push(p); | |
| } | |
| var duration = 30000; | |
| var nodes = []; | |
| paths.forEach(function(d, i){ | |
| var path = svg.append("path") | |
| .data([d]) | |
| .attr("d", d3.svg.line()); | |
| var node = svg.append("g") | |
| .classed("node", true) | |
| .attr("transform", "translate(" + d[0] + ")"); | |
| node.append("circle") | |
| .attr("r", 0) | |
| .style("fill", "#00ff00") | |
| .classed("center", true); | |
| node.append("circle") | |
| .attr("r", 24) | |
| .classed("glow", true); | |
| var delay = Math.random() * duration; | |
| nodes.push({elem: node, path: path, delay: delay, firstRender: true, draw: true}); | |
| }); | |
| var sortedNodes = _.sortBy(nodes, "delay"); | |
| sortedNodes.forEach(function(d, i){ | |
| if (i > 0) { | |
| d.elem.append("line") | |
| .classed("line", true); | |
| /* | |
| d.elem.append("text") | |
| .text(i); | |
| */ | |
| } | |
| transition(d, i > 0 ? sortedNodes[i-1] : null); | |
| }); | |
| function transition(node, prevNode) { | |
| var dur = node.firstRender ? (duration - node.delay) + node.delay : duration; | |
| var del = node.firstRender ? 0 : node.delay; | |
| node.elem.transition() | |
| .duration(dur) | |
| .delay(del) | |
| .ease("linear") | |
| .attrTween("transform", translateAlong(node, prevNode)) | |
| .each("end", function(){ | |
| transition(node, prevNode); | |
| }); | |
| } | |
| function translateAlong(node, prevNode) { | |
| var path = node.path.node(); | |
| var l = path.getTotalLength(); | |
| var scaleOffset = (Math.random() * 12); | |
| var x = d3.scale.linear() | |
| .domain([0, .6, 1]) | |
| .range([0, scaleOffset + 8, scaleOffset + 4]); | |
| var elem = node.elem; | |
| return function() { | |
| return function(t) { | |
| var t_offset = node.firstRender ? (duration - node.delay)/duration + t : t; | |
| t_offset = t_offset > 1 ? t_offset - 1 : t_offset; | |
| elem.selectAll(".center") | |
| .attr("r", function(){ return x(t_offset);}); | |
| var p = path.getPointAtLength(t_offset * l); | |
| if (prevNode) { | |
| var line = elem.selectAll("line"); | |
| var prevTransform = d3.transform(prevNode.elem.attr("transform")).translate; | |
| var currTransform = [p.x, p.y]; | |
| var distance = Math.sqrt(Math.pow(prevTransform[0] - currTransform[0], 2) + Math.pow(prevTransform[1] - currTransform[1], 2)); | |
| var draw = distance < 160 && prevTransform[0] > 320; | |
| line.attr("x2", function(){ return draw ? prevTransform[0] - currTransform[0] : 0 }) | |
| .attr("y2", function(){ return draw ? prevTransform[1] - currTransform[1] : 0 }) | |
| .style("stroke-width", function(){ return distance > 140 ? "1px" : "2px"}); | |
| if (draw && node.startGlow) { | |
| elem.selectAll(".glow") | |
| .attr("r", function(){ return x(t_offset) * 5;}) | |
| .classed("active", true); | |
| } | |
| if (!draw) { | |
| elem.selectAll(".glow") | |
| .classed("active", false); | |
| node.startGlow = true; | |
| } | |
| node.draw = draw; | |
| } | |
| return "translate(" + p.x + "," + p.y + ")"; | |
| }; | |
| }; | |
| } | |
| </script> | |
| </script> | |
| </body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment