Public
Edited
Mar 7, 2023
Insert cell
Insert cell
{
const w = Math.min(width, 800);
const h = Math.min(w, 400);
const ctx = DOM.context2d(w, h)

const toggle = raf(makeLoop(ctx, w, h))
invalidation.then(() => toggle(false));
// A demonstration of using toggle() to play/pause/replace the loop

const bPlay = html`<button>play`;
bPlay.onclick = () => toggle(true);

const bPause = html`<button>pause`;
bPause.onclick = () => toggle(false);
const bToggle = html`<button>toggle`;
bToggle.onclick = () => toggle();
const bReplace = html`<button>replace`;
bReplace.onclick = () => {ctx.clearRect(0, 0, w, h);toggle(makeLoop(ctx, w, h))};
return html`${bPlay}${bPause}${bToggle}${bReplace}<hr>${ctx.canvas}`;
}

Insert cell
function raf(loop){
let request = null, running = false;
const r = t => window.requestAnimationFrame(t);
const c = r => window.cancelAnimationFrame(r);
const tick = () => {
loop();
request = running ? r(tick) : null;
};
// call this to toggle the loop, or force it to start or stop,
// or to replace the loop function
const toggle = (forced) => {
if (forced !== undefined) {
if (running === forced || running && forced === loop) return;
if (typeof forced === "function") {
if (running) c(request);
loop = forced;
}
running = forced ? true : false;
} else running = !running;
request = running ? r(tick) : (c(request),null);
return running;
};

toggle();

return toggle;
}
Insert cell
// Just some silly animation code to demonstrate the above helper function
makeLoop = (ctx, width, height) => {
const hypnoSquare = (x, y, w, h, period = 300) => {
let frames = 0;
period = Number.isFinite(period) && period > 0 ? period : 1;
const offset = Math.PI * 2 * Math.random()
return () => {
let step = Math.cos(frames++ / period + offset);
step = step * step * 10 + 5;
for (let i = 0, flip = true; i < Math.min(w/2, h/2); i += step) {
ctx.fillStyle = (flip = !flip) ? "black" : "white";
ctx.fillRect(x + i, y + i, w - 2*i, h - 2*i);
}
}
}
const randomCoordinates = () => {
let x1, x2, y1, y2, x, y, w = 0, h = 0;
while (w < width / 16 || w > width / 4) {
x1 = Math.random() * width;
x2 = Math.random() * width;
x = Math.min(x1, x2);
w = Math.abs(x1 - x2);
}
while (h < height / 16 || h > height / 4) {
y1 = Math.random() * height;
y2 = Math.random() * height;
y = Math.min(y1, y2);
h = Math.abs(y1 - y2);
}
const period = 100 + 300 * Math.random();
return {x, y, w, h, period};
};

const squares = [];

for (let i = 0; i < 100; i++) {
const {x, y, w, h, period} = randomCoordinates();
squares.push(hypnoSquare(x, y, w, h, period));
}
return () => {for (const sq of squares) sq()};
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more