'use client' import clsx from "clsx"; import Image from "next/image"; import { useCallback, useEffect, useRef, useState } from "react" interface WobblingImageInterface { images: { idle: string; aware?: string; poked?: string; weird?: string; } } function WobblingImage ({ images }: WobblingImageInterface) { const size = 400; const [isPoked, setPoked] = useState(false); const [isAware, setAware] = useState(false); const pokeTimeoutRef = useRef(null); const [isAnimating, setIsAnimating] = useState(false); const audioPath = "/sound/poke.wav"; const audioContextRef = useRef(null); const [audioBuffer, setAudioBuffer] = useState(null); const [audioLoaded, setAudioLoaded] = useState(false); const [dummyAudio, setDummyAudio] = useState(null); useEffect(() => { if (typeof Audio !== undefined) setDummyAudio(new Audio(audioPath)) return () => { if (dummyAudio) dummyAudio.pause(); } }, []) const initializeAudioContext = useCallback((): AudioContext => { if (!audioContextRef.current) { audioContextRef.current = new (window.AudioContext || (window as any).webkitAudioContext)(); } return audioContextRef.current; }, []); const loadAudio = useCallback(async (url: string): Promise => { const response = await fetch(url); const arrayBuffer = await response.arrayBuffer(); const context = initializeAudioContext(); return await context.decodeAudioData(arrayBuffer); }, [initializeAudioContext]); const initializeAudio = useCallback(async (): Promise => { if (audioBuffer) return; setAudioLoaded(false); try { const buffer = await loadAudio(audioPath); setAudioBuffer(buffer); } catch (error) { console.error('Failed to load audio:', error); } finally { setAudioLoaded(true); } }, [audioBuffer, loadAudio]); const playRandomPitch = useCallback(async (): Promise => { await initializeAudio(); if (!audioBuffer || !audioContextRef.current) return; const context = audioContextRef.current; const changeToLowPitch = Math.floor(Math.random() * 66); const randomPitch = Math.random() * 0.38 + (changeToLowPitch == 1 ? 0.25 : 0.75); const source = context.createBufferSource(); source.buffer = audioBuffer; source.playbackRate.value = randomPitch; source.connect(context.destination); source.start(0); source.onended = () => { source.disconnect(); }; }, [audioBuffer, initializeAudio]); function handleClick() { const pokedDuration = 700 + Math.floor(Math.random() * 100) if (!isAware) setAware(true); setIsAnimating(false); requestAnimationFrame(() => setIsAnimating(true)); playRandomPitch(); if (dummyAudio && !audioLoaded) { dummyAudio.currentTime = 0; dummyAudio.play(); } if (pokeTimeoutRef.current) { clearTimeout(pokeTimeoutRef.current); } setPoked(true); pokeTimeoutRef.current = setTimeout(() => { setPoked(false); }, pokedDuration); } function handleAnimationEnd() { setIsAnimating(false); } return (
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleClick(); } }} onMouseDown={(e) => e.preventDefault()} onPointerDown={(e) => e.preventDefault()} > clip1 clip2
) } export default WobblingImage;