Published
Edited
Jun 2, 2020
3 stars
Insert cell
Insert cell
blue_circle = {
// cx = location | cr = radius
const anim_circle = minanim.const("cx", 100)
.seq(minanim.const("cr", 0))
// (1) grow in size.
.seq(minanim.interpolated(minanim.ease_cubic, "cr", /*val=*/10, /*time=*/3))
// (2) go to right while growing.
.seq(minanim.interpolated(minanim.ease_cubic, "cx", /*val=*/300, /*time=*/1)
.par(minanim.interpolated(minanim.ease_cubic, "cr", 70, 1)))
// (3) pause.
.seq(minanim.delay(/*time=*/3))
// (4) come back to the left.
.seq(minanim.interpolated(minanim.ease_cubic, "cx", 100, 1))
// (5) pause again.
.seq(minanim.delay(/*time=*/2))
// (6) shrink to nothing.
.seq(minanim.interpolated(minanim.ease_cubic, "cr", 0, 1))
.seq(minanim.delay(/*time=*/3));
const ctx = DOM.context2d(400, 200);
let t = 0;
const out = {cx: 0, cr: 0};
const totalSeconds = 60 * 6;
const norm = anim_circle.duration / totalSeconds
while(true) {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 400, 200);
ctx.beginPath();
ctx.fillStyle = "#1a73e8";
anim_circle(t * norm, out);
ctx.arc(out.cx, 100, out.cr, 0, Math.PI * 2);
ctx.fill();
yield ctx.canvas
t = (t + 1) % totalSeconds;
}
}
Insert cell
delayed_circles = {
const ctx = DOM.context2d(400, 200);
const anim_circles_start = [];
const anim_circles_enter = [];
const anim_circles_leave = [];
const STAGGER = 80;
const NCIRCLES = 10;
for (let i = 0; i < NCIRCLES; i++) {
anim_circles_start.push(
minanim.const("cx" + i, 10 + i*20)
.seq(minanim.const("cy" + i, 300))
.seq(minanim.const("cr" + i, 7))
);
anim_circles_enter.push(minanim.interpolated(minanim.ease_cubic, "cy" + i, 100, 300));
anim_circles_leave.push(minanim.interpolated(minanim.ease_cubic, "cr" + i, 0, 800));
}

const anim = minanim.parallel_list(anim_circles_start)
.seq(minanim.stagger([
minanim.stagger(anim_circles_enter, STAGGER),
minanim.stagger(anim_circles_leave, STAGGER)
], 300));

const totalSeconds = 60 * 10;
const norm = anim.duration / totalSeconds
let t = 0;
while(true) {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 400, 200);
ctx.beginPath();
ctx.fillStyle = "#1a73e8";
const out = anim(t * norm, {});
for (let i = 0; i < NCIRCLES; i++) {
const cx = out["cx" + i], cy = out["cy" + i];
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, out["cr" + i], 0, Math.PI * 2);
}
ctx.fill();
yield ctx.canvas
t = (t + 1) % totalSeconds;
}
}
Insert cell
Insert cell
minanim = {
function assert_precondition(t, out, tstart) {
console.assert(typeof(t) === "number");
if (out === undefined) { out = {}; }
console.assert(typeof(out) === "object");
if (tstart === undefined) { tstart = 0; }
else { console.assert(typeof(tstart) === "number"); }
console.assert(t >= tstart);
return [out, tstart];
}

function anim_delay(duration) {
console.assert(typeof(duration) === "number");
let f = function(t, out, tstart) {
[out, tstart] = assert_precondition(t, out, tstart); return out;
}
f.duration = duration;
f.par = ((g) => anim_parallel(f, g));
f.seq = ((g) => anim_sequence(f, g));
return f;
}

function anim_const(field, v) {
let f = function(t, out, tstart) {
[out, tstart] = assert_precondition(t, out, tstart); out[field] = v; return out;
};
f.duration = 0;
f.par = ((g) => anim_parallel(f, g));
f.seq = ((g) => anim_sequence(f, g));
return f;
}

function ease_linear(vstart, tlin, vend) { return (1.0 - tlin) * vstart + tlin * vend; }

function ease_cubic(vstart, tlin, vend) {
const cube = (1 - tlin)*(1-tlin)*(1-tlin); return cube * vstart + (1 - cube) * vend;
}
function ease_out_back(vstart, tlin, vend) {
const c1 = 1.70158; const c3 = c1 + 1; const t = 1 + c3 * Math.pow(tlin - 1, 3) + c1 * Math.pow(tlin - 1, 2);
return (1-t) * vstart + t*vend;
}


function anim_interpolated(fease, field, vend, duration) {
let f = function(t, out, tstart) {
[out, tstart] = assert_precondition(t, out, tstart);
if (t < tstart + duration && duration !== 0) {
const tlin = (t - tstart) /duration;
console.assert(tlin >= 0);
console.assert(tlin <= 1);
const vstart = out[field];
out[field] = fease(vstart, tlin, vend);
} else { out[field] = vend; }
return out;
};
f.duration = duration;
f.par = ((g) => anim_parallel(f, g));
f.seq = ((g) => anim_sequence(f, g));
return f;

}

function anim_sequence(anim1, anim2) {
const duration = anim1.duration + anim2.duration;
let f = function(t, out, tstart) {
[out, tstart] = assert_precondition(t, out, tstart);
anim1(t, out, tstart);
if (t >= tstart + anim1.duration) { anim2(t, out, tstart + anim1.duration); }
return out;
}
f.duration = duration;
f.par = ((g) => anim_parallel(f, g));
f.seq = ((g) => anim_sequence(f, g));
return f;
}

function anim_parallel(anim1, anim2) {
const duration = Math.max(anim1.duration, anim2.duration);
let f = function(t, out, tstart) {
[out, tstart] = assert_precondition(t, out, tstart);
if (t >= tstart) { anim1(t, out, tstart); anim2(t, out, tstart); }
return out;
}
f.duration = duration;
f.par = ((g) => anim_parallel(f, g));
f.seq = ((g) => anim_sequence(f, g));
return f;
}

function anim_sequence_list(xs) { var x = xs[0]; for(var i = 1; i < xs.length; ++i) { x = x.seq(xs[i]); } return x; }
function anim_parallel_list(xs) { var x = xs[0]; for(var i = 1; i < xs.length; ++i) { x = x.par(xs[i]); } return x; }

function anim_stagger(xs, delta) {
console.assert(typeof(delta) == "number");
var ys = [];
for(var i = 0; i < xs.length; ++i) {
ys.push(anim_delay(delta*i).seq(xs[i]));
}
var y = ys[0];
for(var i = 1; i < ys.length; ++i) {
y = y.par(ys[i]);
}
return y;
}

return {
assert_precondition,
delay: anim_delay,
const: anim_const,
ease_linear,
ease_cubic,
ease_out_back,
interpolated: anim_interpolated,
sequence: anim_sequence,
parallel: anim_parallel,
sequence_list: anim_sequence_list,
parallel_list: anim_parallel_list,
stagger: anim_stagger,
}
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more