/* The interface is like a flywheel. */ export class Flywheel { constructor() { this.value = 0.; // start at 0 - gradual meter this.stage = 0.; // start at 0 - the various stages this.f_falloff = (value) => value / 20; // value at which it reduces per second this.f_stage = (value) => 5 * Math.log10(value); // value to stage calculation this.value_min = 0; this.value_max = 100; // gives stage 0-9 // this.interval = window.setInterval(this.step, 50) this.time = null; window.requestAnimationFrame(this.step.bind(this)); // TODO: once/twice per second should be enough! + on release (add()) this.effects = { 'shape': new ShapeEffect(), 'video': new VideoEffect(), } } step(time) { if (this.time === null) { // first run this.time = time; window.requestAnimationFrame(this.step.bind(this)); return; } if (time - this.time < 500) { window.requestAnimationFrame(this.step.bind(this)); return; } const dt = time - this.time; this.time = time; this.value -= this.f_falloff(this.value) * dt / 1000; // linear falloff if (this.value < 0) { this.value = 0; } // console.log(this.value,this.falloff, dt,this.time); // TODO: check if this should be different const stage = Math.max(0, Math.floor(this.f_stage(this.value))); if (stage != this.stage) { this.changeStage(stage); } window.requestAnimationFrame(this.step.bind(this)); } add(increment) { this.value += typeof increment == 'undefined' ? 1 : increment; // cap: if (this.value > this.value_max) this.value = this.value_max; } changeStage(stage) { this.stage = stage; console.log('stage now at', this.stage); for (let fx of Object.values(this.effects)) { fx.setStageHandler(this.stage); } } } function effect(start_stage, end_stage) { return class Effect { constructor() { this.enabled = false; this.start_stage = start_stage; this.end_stage = end_stage; this.intensity = 0; this.intensity_easing = (t) => t; // 0 < t <= 1: use this to make effect eg. more eased } setStageHandler(stage) { if (stage < this.start_stage || stage > this.end_stage) { this.disable(stage); } else { this.enable(stage); } } enable(stage) { if (!this.enabled) { this.enabled = true; this.enableHandler(); } const ds = this.end_stage - this.start_stage + 1; if (ds === 1) { this.setIntensity(1); } else { const s = stage - this.start_stage + 1; const t = s / ds; this.setIntensity(this.intensity_easing(t)); } } disable(stage) { if (this.enabled) { this.enabled = false; this.disableHandler(); } } setIntensity(intensity) { if (intensity !== this.intensity) { this.intensity = intensity; this.intensityHandler(); } } enableHandler() {/* to override */ } disableHandler() {/* to override */ } intensityHandler() {/* to override */ } } } class ShapeEffect extends effect(3, 10) { getAngles() { if (!this.enabled) { return 4; } if (this.intensity < .3) { return 3; } else if (this.intensity < .7) { return 5; } else { // random pick const corners = [3, 4, 5, 7] return corners[Math.floor(Math.random() * corners.length)]; } } } class VideoEffect extends effect(3, 10) { // override enable instead of using handler to apply it gradually disableHandler() { document.getElementById('video_background').style.opacity = 0; } intensityHandler() { document.getElementById('video_background').style.opacity = this.intensity; } }