Use the sliders to adjust the number of boxes and the speed of rotation. Drag the "Rotation speed" slider all the way to the left to disable rotation.
Inspired by this YouTube video.
For the rainbow version, see this block.
license: gpl-3.0 |
Use the sliders to adjust the number of boxes and the speed of rotation. Drag the "Rotation speed" slider all the way to the left to disable rotation.
Inspired by this YouTube video.
For the rainbow version, see this block.
<html> | |
<head> | |
<style> | |
body { | |
margin: 0; | |
font-family: "Helvetica Neue", sans-serif; | |
} | |
.box { | |
stroke: #fff; | |
fill: #3a403d; | |
} | |
.label-wrapper { | |
top: 10px; | |
position: absolute; | |
width: 20%; | |
} | |
.label-wrapper.rotation-speed { | |
left: 20%; | |
} | |
.label { | |
width: 100%; | |
margin-left: 5%; | |
text-shadow: -3px -3px 1px rgba(255, 255, 255, 0.4), -3px -2px 1px rgba(255, 255, 255, 0.4), -3px -1px 1px rgba(255, 255, 255, 0.4), -3px 0px 1px rgba(255, 255, 255, 0.4), -3px 1px 1px rgba(255, 255, 255, 0.4), -3px 2px 1px rgba(255, 255, 255, 0.4), -3px 3px 1px rgba(255, 255, 255, 0.4), -2px -3px 1px rgba(255, 255, 255, 0.4), -2px -2px 1px rgba(255, 255, 255, 0.4), -2px -1px 1px rgba(255, 255, 255, 0.4), -2px 0px 1px rgba(255, 255, 255, 0.4), -2px 1px 1px rgba(255, 255, 255, 0.4), -2px 2px 1px rgba(255, 255, 255, 0.4), -2px 3px 1px rgba(255, 255, 255, 0.4), -1px -3px 1px rgba(255, 255, 255, 0.4), -1px -2px 1px rgba(255, 255, 255, 0.4), -1px -1px 1px rgba(255, 255, 255, 0.4), -1px 0px 1px rgba(255, 255, 255, 0.4), -1px 1px 1px rgba(255, 255, 255, 0.4), -1px 2px 1px rgba(255, 255, 255, 0.4), -1px 3px 1px rgba(255, 255, 255, 0.4), 0px -3px 1px rgba(255, 255, 255, 0.4), 0px -2px 1px rgba(255, 255, 255, 0.4), 0px -1px 1px rgba(255, 255, 255, 0.4), 0px 1px 1px rgba(255, 255, 255, 0.4), 0px 2px 1px rgba(255, 255, 255, 0.4), 0px 3px 1px rgba(255, 255, 255, 0.4), 1px -3px 1px rgba(255, 255, 255, 0.4), 1px -2px 1px rgba(255, 255, 255, 0.4), 1px -1px 1px rgba(255, 255, 255, 0.4), 1px 0px 1px rgba(255, 255, 255, 0.4), 1px 1px 1px rgba(255, 255, 255, 0.4), 1px 2px 1px rgba(255, 255, 255, 0.4), 1px 3px 1px rgba(255, 255, 255, 0.4), 2px -3px 1px rgba(255, 255, 255, 0.4), 2px -2px 1px rgba(255, 255, 255, 0.4), 2px -1px 1px rgba(255, 255, 255, 0.4), 2px 0px 1px rgba(255, 255, 255, 0.4), 2px 1px 1px rgba(255, 255, 255, 0.4), 2px 2px 1px rgba(255, 255, 255, 0.4), 2px 3px 1px rgba(255, 255, 255, 0.4), 3px -3px 1px rgba(255, 255, 255, 0.4), 3px -2px 1px rgba(255, 255, 255, 0.4), 3px -1px 1px rgba(255, 255, 255, 0.4), 3px 0px 1px rgba(255, 255, 255, 0.4), 3px 1px 1px rgba(255, 255, 255, 0.4), 3px 2px 1px rgba(255, 255, 255, 0.4), 3px 3px 1px rgba(255, 255, 255, 0.4); | |
} | |
input { | |
width: 90%; | |
margin-left: 5%; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="canvas"> | |
<div class="label-wrapper box-count"> | |
<div class="label">Box count</div> | |
<input class="count" type="range" min="1" max="100" value="50"></input> | |
</div> | |
<div class="label-wrapper rotation-speed"> | |
<div class="label">Rotation speed</div> | |
<input class="speed" type="range" min="0" max="3" value="1" step=".1"></input> | |
</div> | |
</div> | |
<script src="https://d3js.org/d3-array.v2.min.js"></script> | |
<script src="https://d3js.org/d3-selection.v1.min.js"></script> | |
<script src="https://d3js.org/d3-interpolate.v1.min.js"></script> | |
<script src="https://d3js.org/d3-timer.v1.min.js"></script> | |
<script src="https://d3js.org/d3-scale.v3.min.js"></script> | |
<script> | |
let width, | |
height, | |
center, | |
l, | |
m, | |
count = 50, | |
speed = 1, | |
rotation = 0; | |
const svg = d3.select("#canvas").append("svg"); | |
const g = svg.append("g"); | |
d3.selectAll("input").on("input", function(){ | |
count = +d3.select("input.count").property("value"); | |
speed = +d3.select("input.speed").property("value"); | |
draw(); | |
}); | |
d3.timer(_ => { | |
rotation += speed; | |
g.attr("transform", "rotate(" + rotation + " " + center[0] + " " + center[1] + ")"); | |
}); | |
size(); | |
onresize = size; | |
function draw(){ | |
const s = d3.scaleLinear().domain([1, count]).range([1, 10]); | |
let sum = 0; | |
const boxes = g.selectAll(".box") | |
.data(d3.range(count + 1)); | |
boxes.exit().remove(); | |
boxes.enter().append("rect") | |
.attr("class", "box") | |
.merge(boxes) | |
.attr("x", d => center[0] - ((m * d / count) / 2)) | |
.attr("y", d => center[1] - ((m * d / count) / 2)) | |
.attr("width", d => m * d / count) | |
.attr("height", d => m * d / count) | |
.attr("transform", d => { | |
sum += d; | |
const theta = sum / d * Math.PI; | |
return theta ? `rotate(${theta + (d * (360 / count))} ${center[0]} ${center[1]})` : null; | |
}) | |
.style("stroke-width", d => s(d)) | |
.lower(); | |
} | |
function size(){ | |
width = innerWidth; | |
height = innerHeight; | |
center = [width / 2, height / 2]; | |
l = Math.min(width, height); | |
m = Math.max(width, height); | |
svg | |
.attr("width", width) | |
.attr("height", height); | |
g | |
.attr("width", l) | |
.attr("height", l); | |
draw(); | |
} | |
</script> | |
</body> | |
</html> |