{
const options = {
width: 900,
height: 400,
centerX: .5,
centerY: .95,
seedSize: 5,
count: 40,
rotationGlobal: .0,
rotationMin: .01,
rotationMax: .2,
rotationSpeed: .0005,
rotationShift: 0,
lightInner: .1,
lightOuter: 1,
lightShift: 1.05,
lineWidth: 2,
lineLight: 1,
lineOpacity: .03,
colorFn: colorAlternate,
rng: Math.random,
};
reset;
const {width: w, height: h, rng} = options;
const clearColor = `hsl(0,0%,${options.lightOuter}%)`;
const color = options.colorFn(options);
const cx = w * options.centerX, cy = h * options.centerY;
const c = DOM.context2d(w, h);
c.canvas.style.maxWidth = '100%';
const {PI} = Math;
const randAngle = (min, max) => mix(min, max, rng()) * PI * 2;
const angles = Array(options.count).fill().map((v,i) => {
return mix(-1, 1, i % 2) * randAngle(options.rotationMin, options.rotationMax);
});
c.strokeStyle = `hsla(0,0%,${options.lineLight * 100}%,${options.lineOpacity})`;
c.lineWidth = options.lineWidth;
let t = 0;
while(true) {
clear();
draw(angles, t);
yield c.canvas;
t += options.rotationGlobal;
angles.forEach((a, i, arr) => {
const t = (i+1)/arr.length ** options.rotationShift;
arr[i] += Math.sign(a) * t * options.rotationSpeed;
});
}
function draw(angles, t = 0) {
let s = options.seedSize, aGlobal = t * PI * 2;
const squares = angles.map((aLocal, i, arr) => ({
size: (s = s * (Math.abs(Math.cos(aLocal)) + Math.abs(Math.sin(aLocal)))),
aGlobal: (aGlobal += aLocal),
aLocal,
}));
for(let i = squares.length - 1; i >= 0; i--) {
const square = squares[i];
c.beginPath();
drawSquare(square);
c.fillStyle = color(square, i);
c.fill();
if(c.lineWidth > 0) c.stroke();
}
}
function drawSquare({size, aGlobal}) {
c.save();
c.translate(cx, cy);
c.rotate(aGlobal);
c.translate(-cx, -cy);
c.rect(cx - size/2, cy - size/2, size, size);
c.restore();
}
function clear() {
c.save();
c.fillStyle = clearColor;
c.fillRect(0, 0, w, h);
c.restore();
}
}