From 802ef033caf1f7090888b10434c8e6a37a9265cd Mon Sep 17 00:00:00 2001 From: crummy Date: Mon, 26 Aug 2024 21:12:02 +0200 Subject: [PATCH] Add a nice ripple effect on touch --- src/components/Theremax.ts | 8 +---- src/components/Visualization.ts | 63 +++++++++++++++++++++++++++++++++ src/components/lerp.ts | 5 +++ 3 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 src/components/lerp.ts diff --git a/src/components/Theremax.ts b/src/components/Theremax.ts index e28a30f..e01f573 100644 --- a/src/components/Theremax.ts +++ b/src/components/Theremax.ts @@ -1,6 +1,6 @@ import {Timer} from "./Timer.ts"; import type {Instrument} from "./player.ts"; -import {getContext} from "svelte"; +import {lerp} from "./lerp.ts"; interface Instant { millis: number @@ -31,12 +31,6 @@ class Recording { } } -function lerp(value: number, sourceRangeMin: number, sourceRangeMax: number, targetRangeMin: number, targetRangeMax: number) { - const targetRange = targetRangeMax - targetRangeMin; - const sourceRange = sourceRangeMax - sourceRangeMin; - return (value - sourceRangeMin) * targetRange / sourceRange + targetRangeMin; -} - export interface TheremaxVisualization { clearLines(): void diff --git a/src/components/Visualization.ts b/src/components/Visualization.ts index 2337735..4a8fb41 100644 --- a/src/components/Visualization.ts +++ b/src/components/Visualization.ts @@ -1,5 +1,6 @@ import {Application, Graphics} from "pixi.js"; import type {TheremaxVisualization} from "./Theremax.ts"; +import {lerp} from "./lerp.ts"; const screenPadding = 16; const colours = [ @@ -38,6 +39,7 @@ export class Visualization implements TheremaxVisualization { private columns: Graphics[] = [] private lines: Graphics[] = [] private progress = new Graphics(); + private ripples = new Ripples() async init(element: HTMLElement) { @@ -71,7 +73,10 @@ export class Visualization implements TheremaxVisualization { column.alpha *= 0.97; } this.tickListener(Date.now()) + this.ripples.tick() }); + + this.app.stage.addChild(this.ripples.graphics) } getWidth() { @@ -136,6 +141,11 @@ export class Visualization implements TheremaxVisualization { const {x, y} = points[i] graphics.lineTo(x, y) } + if (points.length > 0) { + const last = points[points.length - 1] + this.ripples.addRipple(last.x, last.y) + } + graphics.stroke({width: 4, color: colours[recordingId % colours.length]}); } @@ -153,4 +163,57 @@ export class Visualization implements TheremaxVisualization { this.app.stage.addChild(this.progress); } +} + +class Ripples { + private readonly maxRipples = 32 + private readonly maxAgeMs = 300 + readonly graphics = new Graphics() + readonly ripples: { x: number, y: number, creation: number, graphics: Graphics }[] = [] + + addRipple(x: number, y: number) { + while (this.ripples.length >= this.maxRipples) { + const stale = this.ripples.shift() + if (!stale) { + throw new Error("Impossible") + } + this.graphics.removeChild(stale.graphics) + } + const creation = Date.now() + const radius = this.radius(creation); + const alpha = this.fade(creation) + const graphics = new Graphics().circle(x, y, radius).fill({ r: 1, g: 255, b: 3, a: alpha}) + this.ripples.push({ x, y, creation, graphics}) + this.graphics.addChild(graphics) + } + + tick() { + const now = Date.now() + while (this.ripples.length > 0 && this.ripples[0].creation + this.maxAgeMs < now) { + // assuming ripples is sorted... + const stale = this.ripples.shift() + if (!stale) { + throw new Error("Impossible") + } + this.graphics.removeChild(stale.graphics) + } + for (const ripple of this.ripples) { + const { x, y, creation } = ripple + const radius = this.radius(creation); + const alpha = this.fade(creation) + ripple.graphics.clear().circle(x, y, radius).stroke({ r: 255, g: 255, b: 3, a: alpha}) + } + } + + fade(creation: number) { + const age = Date.now() - creation + if (age === 0) { + return 1; + } + return lerp(age, 0, this.maxAgeMs, 1, 0) + } + + radius(creation: number) { + return (Date.now() - creation) + } } \ No newline at end of file diff --git a/src/components/lerp.ts b/src/components/lerp.ts new file mode 100644 index 0000000..ce3fd0f --- /dev/null +++ b/src/components/lerp.ts @@ -0,0 +1,5 @@ +export function lerp(value: number, sourceRangeMin: number, sourceRangeMax: number, targetRangeMin: number, targetRangeMax: number) { + const targetRange = targetRangeMax - targetRangeMin; + const sourceRange = sourceRangeMax - sourceRangeMin; + return (value - sourceRangeMin) * targetRange / sourceRange + targetRangeMin; +} \ No newline at end of file