Basic Effects

This commit is contained in:
Ruben van de Ven 2020-12-17 13:22:58 +01:00
parent a56fac5bfd
commit 0679766f6c
4 changed files with 166 additions and 74 deletions

158
js/effects.js vendored Normal file
View file

@ -0,0 +1,158 @@
/*
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;
}
}

View file

@ -1,66 +0,0 @@
/*
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())
}
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);
// TODO: handle stage change
}
}

View file

@ -1,19 +1,16 @@
import { Flywheel } from "./flywheel.js"; import { Flywheel } from "./effects.js";
import { getPathShapeBetweenPoints } from "./svg.js"; import { getPathShapeBetweenPoints } from "./svg.js";
export function init(svgEl, crosshairXEl, crosshairYEl) { export function init(svgEl, crosshairXEl, crosshairYEl) {
let flywheel = new Flywheel(); let flywheel = new Flywheel();
// use ?3 and ?4 ?28 etc in query string to determine segments
const segmentCount = window.location.search.substr(1).length ? window.location.search.substr(1) : 4; const segmentCount = window.location.search.substr(1).length ? window.location.search.substr(1) : 4;
// canvas position relative to mouse/screen // canvas position relative to mouse/screen
// TODO: update on resize: // TODO: update on resize:
svgEl.setAttribute('viewBox', '0 0 ' + window.innerWidth + ' ' + window.innerHeight); svgEl.setAttribute('viewBox', '0 0 ' + window.innerWidth + ' ' + window.innerHeight);
let rect = svgEl.getBoundingClientRect(); let rect = svgEl.getBoundingClientRect();
// let cx = rect.left + rect.width / 2;
// let cy = rect.top + rect.height / 2;
// let center = { 'x': cx, 'y': cy };
// move crosshairs // move crosshairs
window.addEventListener('mousemove', function (mousemoveEv) { window.addEventListener('mousemove', function (mousemoveEv) {
@ -27,16 +24,18 @@ export function init(svgEl, crosshairXEl, crosshairYEl) {
}); });
svgEl.addEventListener('mousedown', function (mousedownEv) { svgEl.addEventListener('mousedown', function (mousedownEv) {
// create shape to draw
let shapeEl = document.createElementNS('http://www.w3.org/2000/svg', 'path'); let shapeEl = document.createElementNS('http://www.w3.org/2000/svg', 'path');
shapeEl.classList.add('rect'); shapeEl.classList.add('rect');
let p1 = { 'x': mousedownEv.clientX - rect.left, 'y': mousedownEv.clientY - rect.top } let p1 = { 'x': mousedownEv.clientX - rect.left, 'y': mousedownEv.clientY - rect.top }
let d = getPathShapeBetweenPoints(p1, p1, segmentCount); let corners = flywheel.effects.shape.getAngles();
let d = getPathShapeBetweenPoints(p1, p1, corners);
svgEl.appendChild(shapeEl); svgEl.appendChild(shapeEl);
shapeEl.setAttribute('d', d); shapeEl.setAttribute('d', d);
let mouseMoveEv = function (mousemoveEv) { let mouseMoveEv = function (mousemoveEv) {
let p2 = { 'x': mousemoveEv.clientX - rect.left, 'y': mousemoveEv.clientY - rect.top } let p2 = { 'x': mousemoveEv.clientX - rect.left, 'y': mousemoveEv.clientY - rect.top }
let d = getPathShapeBetweenPoints(p1, p2, segmentCount); let d = getPathShapeBetweenPoints(p1, p2, corners);
shapeEl.setAttribute('d', d); shapeEl.setAttribute('d', d);
}; };
let mouseUpEv = function () { let mouseUpEv = function () {

View file

@ -5,4 +5,5 @@ let crosshairXEl = document.getElementById('crosshair_x');
let crosshairYEl = document.getElementById('crosshair_y'); let crosshairYEl = document.getElementById('crosshair_y');
let game = init(svgEl, crosshairXEl, crosshairYEl); var game = init(svgEl, crosshairXEl, crosshairYEl);
console.log('Initialised game', game);