ちょっとこんな感じではないかと思いますので、参考までに。
Web Audio APIのオシレーターは、一度 stop()
が呼ばれるとそのオシレーターは使い終わったものとみなされ、再利用することはできません。したがって、mousedown
で start()
を呼び出し、mouseup
で stop()
を呼び出すという処理を複数回行いたい場合、毎回新しいオシレーターを生成する必要があります。
参考コードはこんな感じ
import { useRef, useState } from "react";
import styles from "./Home.module.css";
const Home = () => {
const audioContext = useRef(new AudioContext());
// オシレーターの作成を関数に抽出します
const createOscillator = () => {
const oscillator = audioContext.current.createOscillator();
oscillator.type = "sine";
oscillator.frequency.setValueAtTime(700, audioContext.current.currentTime);
oscillator.connect(audioContext.current.destination);
return oscillator;
}
// 初期状態ではnullとしておき、mousedownイベントで作成します
const [oscillator, setOscillator] = useState(null);
const [isTouching, setIsTouching] = useState(0);
const mouseDown = () => {
const newOscillator = createOscillator();
newOscillator.start();
console.debug("start");
setOscillator(newOscillator); // 作成したオシレーターを状態として保持します
setIsTouching(1);
};
const mouseUp = () => {
if(oscillator) { // 万が一nullの場合を考慮
oscillator.stop();
console.debug("stop");
setOscillator(null); // オシレーターを停止したらnullにリセットします
setIsTouching(0);
}
};
return (
<main className={styles.main}>
<div>
<p>touching: {isTouching}</p>
</div>
<div
onMouseDown={mouseDown}
onMouseUp={mouseUp}
className="box-border h-32 w-32 p-4 border-4"
id="touching-box"
/>
</main>
);
};
export default Home;
mousedown
イベントが発生する度に新しいオシレーターが生成され、mouseup
イベントでそのオシレーターが停止する、という流れを実現できるかと。
Like!