Based on this GIF.
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <style> | |
| path { | |
| stroke-width: 2px; | |
| stroke: #444; | |
| fill: none; | |
| } | |
| line { | |
| stroke-width: 4px; | |
| stroke-linecap: round; | |
| } | |
| circle { | |
| stroke: none; | |
| fill: #444; | |
| } | |
| .done line, | |
| .done circle { | |
| display: none; | |
| } | |
| .done path { | |
| stroke-width: 12px; | |
| fill: url("#crosshatch"); | |
| stroke: #0c238d; | |
| } | |
| </style> | |
| <body> | |
| <svg> | |
| <defs> | |
| <pattern id="crosshatch" patternUnits="userSpaceOnUse" width="16" height="16"> | |
| <image xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+CjxyZWN0IHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iI2ZmZiI+PC9yZWN0Pgo8cGF0aCBkPSJNMCAwTDE2IDE2Wk0xNiAwTDAgMTZaIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZT0iIzBjMjM4ZCI+PC9wYXRoPgo8L3N2Zz4=" | |
| x="0" y="0" width="16" height="16"> | |
| </image> | |
| </pattern> | |
| </defs> | |
| </svg> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.min.js"></script> | |
| <script> | |
| var margin = { left: 10, right: 10, top: 10, bottom: 10 }, | |
| width = 960, | |
| height = 500, | |
| duration = 10000, | |
| size = Math.min(width - margin.left - margin.right, height - margin.top - margin.bottom); | |
| var svg = d3.select("svg") | |
| .attr("width", width) | |
| .attr("height", height) | |
| .append("g") | |
| .attr("transform", "translate(" + ((width - size) / 2) + " " + ((height - size) / 2) + ")"); | |
| var colors = ["#ae3cb4", "#ff7146", "#b3e94e"]; | |
| var trace = svg.append("path") | |
| .attr("fill-rule", "evenodd") | |
| .datum("M" + size + "," + (size / 2)); | |
| var arms = getArms(); | |
| var segments = svg.selectAll("line") | |
| .data(arms) | |
| .enter() | |
| .append("line") | |
| .style("stroke",function(d,i){ | |
| return colors[i]; | |
| }); | |
| var pen = svg.append("circle") | |
| .attr("r", 4); | |
| var t = 0; | |
| requestAnimationFrame(update); | |
| function update() { | |
| segments.each(function(arm, i){ | |
| var rotation = Math.PI * 2 * t * arm.speed; | |
| arm.start = i ? arms[i - 1].end : [size / 2, size / 2]; | |
| arm.end = [ | |
| arm.start[0] + arm.length * Math.cos(rotation), | |
| arm.start[1] + arm.length * Math.sin(rotation) | |
| ]; | |
| d3.select(this) | |
| .attr({ | |
| x1: arm.start[0], | |
| y1: arm.start[1], | |
| x2: arm.end[0], | |
| y2: arm.end[1] | |
| }) | |
| }); | |
| var last = arms[arms.length - 1].end; | |
| trace.datum(trace.datum() + "L" + last.join(",")) | |
| .attr("d", Object); | |
| pen.attr("cx", last[0]) | |
| .attr("cy", last[1]); | |
| if (t >= duration) { | |
| trace.datum(trace.datum() + "Z"); | |
| svg.classed("done", true); | |
| return true; | |
| } | |
| // Manually ticking frames | |
| t += 1000 / 60; | |
| requestAnimationFrame(update); | |
| } | |
| function getArms() { | |
| var lengths = [160, 106, 25]; | |
| var rotations = [1,5,21]; | |
| var totalLength = d3.sum(lengths); | |
| return lengths.map(function(l,i){ | |
| return { | |
| length: (size / 2) * l / totalLength, | |
| speed: -rotations[i] / duration | |
| }; | |
| }); | |
| } | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
