feat(web): react wave and ambient visuals to live audio
ci / ts / ts (packages + web) (push) Failing after 2m2s
ci / ts / ts (packages + web) (push) Failing after 2m2s
This commit is contained in:
1 parent
9223516547
commit
4c2286e70d
8 files changed
+144
-7
No files matched your search
@@ -5,6 +5,7 @@ import { drawLiquid, drawOrb, step, type WaveState } from './draw';
|
||||
export interface WaveButtonProps {
|
||||
variant?: 'orb' | 'liquid';
|
||||
playing?: boolean;
|
||||
readAudio?: () => { bass: number; mid: number; treble: number };
|
||||
bpm?: number;
|
||||
energy?: number;
|
||||
colors?: [string, string, string];
|
||||
@@ -28,6 +29,8 @@ function prefersReducedMotion(): boolean {
|
||||
*/
|
||||
export function WaveButton(props: WaveButtonProps) {
|
||||
const size = props.size ?? 320;
|
||||
const audioRef = useRef(props.readAudio);
|
||||
audioRef.current = props.readAudio;
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const labelRef = useRef<HTMLDivElement>(null);
|
||||
const wrapRef = useRef<HTMLDivElement>(null);
|
||||
@@ -55,6 +58,7 @@ export function WaveButton(props: WaveButtonProps) {
|
||||
const onMq = () => (st.current.reduced = prefersReducedMotion());
|
||||
mq?.addEventListener?.('change', onMq);
|
||||
let raf = 0;
|
||||
let livePulse = 0;
|
||||
let last = performance.now();
|
||||
const loop = (now: number) => {
|
||||
const dt = Math.min(0.05, (now - last) / 1000);
|
||||
@@ -67,12 +71,23 @@ export function WaveButton(props: WaveButtonProps) {
|
||||
cv.height = Math.round(S * dpr);
|
||||
}
|
||||
step(w, dt);
|
||||
const audio = !w.reduced && w.playing ? audioRef.current?.() : undefined;
|
||||
if (audioRef.current && !w.reduced) {
|
||||
const target = audio ? Math.min(1, audio.bass * 0.7 + audio.mid * 0.2) : 0;
|
||||
livePulse += (target - livePulse) * (1 - Math.exp(-dt * (target > livePulse ? 35 : 9)));
|
||||
w.pulse = livePulse;
|
||||
w.amp = audio ? Math.min(1, audio.mid * 0.8 + audio.treble * 0.7) : 0;
|
||||
}
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
ctx.clearRect(0, 0, S, S);
|
||||
const sc = 1 - w.press * 0.07 + w.hover * 0.02;
|
||||
const r = w.size * 0.5 * sc;
|
||||
const r = w.size * 0.5 * sc * (1 + w.pulse * 0.055);
|
||||
const cx = S / 2 + w.mx * w.hover * 6;
|
||||
const cy = S / 2 + w.my * w.hover * 6;
|
||||
const ripple = audio ? w.amp * 0.025 * Math.sin(w.t * 13) : 0;
|
||||
ctx.translate(cx, cy);
|
||||
ctx.scale(1 + ripple, 1 - ripple);
|
||||
ctx.translate(-cx, -cy);
|
||||
if (w.variant === 'liquid') drawLiquid(ctx, cx, cy, r, w, w.t);
|
||||
else drawOrb(ctx, cx, cy, r, w, w.t);
|
||||
if (labelRef.current) labelRef.current.style.transform = `scale(${sc + w.pulse * 0.03})`;
|
||||
|
||||
Reference in new issue
Block a user