{
const size = 640;
const shapes = [];
const random = d3.randomUniform(-25, 25);
let [dx0, dy0, dx1, dy1, i0, i1, j0, j1] = [0, 0, 0, 0, 0, 0, 0, 0];
drawShape(size / 2, size / 2, 150);
const input = d3.extent(shapes.map((d) => d.r)).reverse();
const scale = (i) => d3.scaleSequential(input, d3[`interpolate${colors[i]}`]);
function drawShape(x, y, r) {
if (r < 4) return;
shapes.push({ x, y, r });
drawShape(x, y, r - 15);
}
function draw({ frameCount }) {
if (frameCount !== 0) {
[dx0, dy0] = [dx1, dy1];
[dx1, dy1] = [random(), random()];
[i0, i1] = [i1, frameCount === 1 ? 0 : d3.randomInt(colors.length)()];
[j0, j1] = [j1, frameCount === 1 ? 0 : d3.randomInt(symbols.length)()];
}
const [c0, c1] = [scale(i0), scale(i1)];
const [s0, s1] = [symbols[j0], symbols[j1]];
return cm.svg("g", shapes, {
transform: (_, i) => `translate(${i * dx0},${i * dy0})`,
transition: (d, i) => [
{
transform: `translate(${i * dx1},${i * dy1})`,
duration: 1000,
delay: i * 20,
ease: d3.easeElastic
}
],
children: (d, i) => {
const p = [d.x - d.r, d.y - d.r, d.r * 2];
const path = () => flubber.interpolate(s0(...p), s1(...p));
return [
cm.svg("path", {
stroke: "#000",
d: s0(...p),
fill: c0(d.r),
strokeWidth: 0.5,
transition: [
{ fill: c1(d.r), duration: 500, delay: i * 25 },
{ d: path, duration: 500, delay: 500 + i * 25 }
]
})
];
}
});
}
const app = cm.app({
draw,
loop: true,
frameRate: 0.5,
width: size,
height: size,
use: { transition: cm.transition }
});
invalidation.then(() => app.dispose());
return app.render();
}