auto-accept/www/js/effects.js

158 lines
4.3 KiB
JavaScript

/*
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;
}
}