3.3 KiB
3.3 KiB
Genuary26 - 2
Prompt: Twelve principles of animation
Conway's Game of Life jazzed up with some d3 transitions.
const WIDTH = 800;
const HEIGHT = 600;
const CELL = 10;
const CELLW = 80;
const CELLH = 60;
const GEN_TIME = 1000;
const INIT_PROB = 0.5;
const restart = { restart: false };
const trigger = view(Inputs.button("Restart"));
trigger;
restart["restart"] = true;
function neighbours(i, j, id) {
const VWRAP = CELLW * (CELLH - 1);
const e = i > 0 ? id - 1 : id + CELLW - 1;
const w = i < CELLW - 1 ? id + 1 : id - CELLW + 1;
const n = [e, id, w].map((v) => j > 0 ? v - CELLW : v + VWRAP);
const s = [e, id, w].map((v) => j < CELLH - 1 ? v + CELLW : v - VWRAP);
return [...n, e, w, ...s];
}
const show_grid = (async function* () {
const grid = [];
for( let j = 0; j < CELLH; j++ ) {
for ( let i = 0; i < CELLW; i++ ) {
const id = j * CELLW + i;
const cell = { id: id, x: (i + 0.5) * CELL, y: (j + 0.5) * CELL, live: Math.random() > INIT_PROB };
cell["n"] = neighbours(i, j, id);
grid.push(cell);
}
}
let i = 0;
while ( true ) {
if( restart["restart"] ) {
grid.forEach((c) => c.live = Math.random() > INIT_PROB);
restart["restart"] = false
}
yield grid.filter((d) => d.live);
i++;
const ngrid = [];
for( const id in grid ) {
const live_n = grid[id].n.filter((i) => grid[i].live).length;
if( grid[id].live ) {
ngrid[id] = ( live_n > 2 && live_n < 4 );
} else {
ngrid[id] = ( live_n === 3 );
}
}
for( const id in grid ) {
grid[id].live = ngrid[id];
}
await new Promise((resolve) => setTimeout(resolve, GEN_TIME));
}
})();
// Set up the svg canvas
const svg = d3.create("svg")
.attr("width", WIDTH)
.attr("height", HEIGHT)
.attr("viewBox", [ 0, 0, WIDTH, HEIGHT ]);
svg.append("clipPath")
.attr("id", "clipRect")
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", WIDTH)
.attr("height", HEIGHT);
const bg_g = svg.append("g")
.attr("id", "background");
bg_g.selectAll("rect")
.data( [ { bg: "white" } ] )
.join("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", WIDTH)
.attr("height", HEIGHT)
.attr("fill", (d) => d.bg)
;
const cells_g = svg.append("g")
.attr("id", "cells");
// .attr("clip-path", "url(#clipRect)");
display(svg.node());
const ease = d3.easeElastic.period(0.4).amplitude(3);
cells_g.selectAll("circle")
.data(show_grid, d => d.id)
.join(
enter => enter.append("circle")
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y)
.attr("fill", "green")
.transition()
.ease(ease)
.duration(1000)
.attr("r", (d) => 10),
update => update.attr("fill", "blue"),
exit => exit
.transition()
.duration(2000)
.attr("r", 0)
.attr("cy", (d) => d.y + 400)
.remove()
);