Public
Edited
May 7, 2023
Importers
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
class TestScene extends Scene {
cam = R();
b = R();
constructor() {
super();
this.i();
}
i() {
withDur(0, () => {
this.cam({h: 21});
this.b({x: 0, y: 0, w: 30, h: 20, t: 'Test', lw: 1});
this.b.fitText().pad(1);
});
return this;
}
a() {
this.b({w: 20});
}
}
Insert cell
ts = new TestScene()
Insert cell
show(ts)
Insert cell
function withDur(t, callback) {
const initDur = Timeline.duration;
Timeline.duration = t * 1000;
callback();
Timeline.duration = initDur;
}
Insert cell
function* show(scene) {
window.onkeydown = function(e) {
if (typeof scene[e.key] === 'function') {
scene[e.key]();
} else if (typeof scene.key === 'function') {
scene.key(e.key);
}
};
const ctx = DOM.context2d(width, width / 1920 * 1024);
while (true) {
const dpi = window.devicePixelRatio;
const width = ctx.canvas.width / dpi;
const height = ctx.canvas.height / dpi;
ctx.clearRect(0, 0, width, height);
ctx.save();
ctx.translate(width / 2, height / 2);
const camera = scene.cam;
const s = height / camera.h;
ctx.scale(s, s);
ctx.translate(-camera.x, -camera.y);
scene.draw(ctx);
ctx.restore();
yield ctx.canvas;
}
}
Insert cell
class RClass {
_x = new Timeline(0);
_y = new Timeline(0);
_w = new Timeline(0);
_h = new Timeline(0);
t = '';
_s = new Timeline(10);
_o = new Timeline(1);
c = 'black';
_lw = new Timeline(1);
l = null;
constructor() {}
fitText() {
this.set({w: this.t.length * this.s * 0.6, h: this.s * 1.02});
return this;
}
pad(d) {
this.set({w: this.w + d * 2, h: this.h + d * 2});
return this;
}
set(props) {
Object.entries(props).forEach(([k, v]) => {
if (this['_' + k] instanceof Timeline) {
this['_' + k].v = v;
} else {
this[k] = v;
}
});
}
draw(ctx) {
if (this.lw) {
ctx.lineWidth = this.lw;
ctx.strokeStyle = this.c;
ctx.strokeRect(
this.x - this.w / 2,
this.y - this.h / 2,
this.w,
this.h,
);
}
if (this.t && this.o) {
ctx.fillStyle = 'black';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = `${this.s}px JetBrains Mono`;
ctx.fillText(this.t, this.x, this.y);
}
}
get x() {
return this._x.v;
}
get y() {
return this._y.v;
}
get w() {
return this._w.v;
}
get h() {
return this._h.v;
}
get s() {
return this._s.v;
}
get o() {
return this._o.v;
}
get lw() {
return this._lw.v;
}
}
Insert cell
function isRObject(x) {
return typeof x === 'function' && x && x.isRObject;
}
Insert cell
function R(...initArgs) {
const obj = new RClass(...initArgs);
const res = new Proxy(function RObj(...args) {
obj.set(...args);
}, {
get(target, p) {
if (p === 'isRObject') {
return true;
}
return obj[p];
},
set(target, p, val) {
obj[p] = val;
return true;
},
apply(target, thisArg, argArray) {
return target.apply(thisArg, argArray);
}
});
return res;
}
Insert cell
class Scene {
getChildRObjs() {
const res = new Set();
function process(val) {
if (val instanceof Scene) {
val.getChildRObjs().forEach(obj => res.add(obj));
} else if (isRObject(val)) {
res.add(val);
} else if (Array.isArray(val)) {
val.forEach(v => process(v));
}
}
Object.entries(this).forEach(([key, val]) => {
if (['cam', 'parent'].includes(key)) return;
process(val);
});
return res;
}
draw(ctx) {
this.getChildRObjs().forEach(obj => obj.draw(ctx));
}
}
Insert cell
function smooth(x) {
return (1 - Math.cos(Math.PI * x)) / 2;
}
Insert cell
function clamp(x) {
return Math.max(0, Math.min(1, x));
}
Insert cell
function prop(x0, x1, x) {
return (x - x0) / (x1 - x0);
}
Insert cell
function lin(x0, x1, t) {
return x0 * (1 - t) + x1 * t;
}
Insert cell
class Timeline {
static duration = 1000;
v0 = 0;
t0 = 0;
v1 = 0;
t1 = 0;
constructor(initVal) {
this.setConst(initVal);
}
setConst(v) {
this.t0 = 0;
this.t1 = 0;
this.v0 = v;
this.v1 = v;
}
setTrans(v, d) {
if (d === 0) {
this.setConst(v);
return;
}
const curV = this.v;
this.t0 = Number(new Date());
this.t1 = this.t0 + d;
this.v0 = curV;
this.v1 = v;
}
get v() {
const t = Number(new Date());
if (t <= this.t0) return this.v0;
if (t >= this.t1) return this.v1;
const r = prop(this.t0, this.t1, t);
return lin(this.v0, this.v1, smooth(r));
}
set v(v) {
this.setTrans(v, Timeline.duration);
}
}
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