AIdentity

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

4_oil.tsx (3111B)


      1 import { Canvas } from "@react-three/fiber";
      2 import { OrbitControls } from "@react-three/drei";
      3 import { OilArtAPI, OilArtPlane } from "./OilArt/OilArtPlane.tsx";
      4 import { useEffect, useRef, useState } from "react";
      5 import * as THREE from "three";
      6 import { StageProps } from "../ctrl/page.tsx";
      7 const AUDIO_SOURCES: Record<number, string> = {
      8   1: "/audio/004-1.flac",
      9   2: "/audio/004-2.flac",
     10 };
     11 const useSequentialAudio = (
     12   initialState: number,
     13   audioSources: Record<number, string>,
     14   onComplete: () => void,
     15 ) => {
     16   const [sceneState, setSceneState] = useState(initialState);
     17   const audioRef = useRef<HTMLAudioElement | null>(null);
     18   const currentAudioUrl = audioSources[sceneState];
     19   useEffect(() => {
     20     if (sceneState > Math.max(...Object.keys(audioSources).map(Number))) {
     21       console.log("All audio finished. Calling onComplete.");
     22       onComplete();
     23       return;
     24     }
     25     if (!currentAudioUrl) {
     26       return;
     27     }
     28     console.log(`Starting setup for state ${sceneState}: ${currentAudioUrl}`);
     29     const existingAudio = audioRef.current;
     30     if (existingAudio) {
     31       existingAudio.pause();
     32     }
     33     const audio = new Audio(currentAudioUrl);
     34     audioRef.current = audio;
     35     const handleAudioEnded = () => {
     36       console.log(`Audio ${sceneState} finished. Transitioning state.`);
     37       setSceneState((prev) => prev + 1);
     38     };
     39     audio.addEventListener("ended", handleAudioEnded);
     40     audio.play().catch((e) => {
     41       console.warn(
     42         `Audio playback failed for state ${sceneState} (requires user interaction):`,
     43         e,
     44       );
     45     });
     46     return () => {
     47       if (audio === audioRef.current) {
     48         // 現在設定したAudioオブジェクトであることを確認
     49         audio.pause();
     50         audio.removeEventListener("ended", handleAudioEnded);
     51       }
     52     };
     53   }, [sceneState, audioSources, onComplete]); // sceneStateとcurrentAudioUrlは基本的に連動するため、sceneStateを依存配列に含める
     54   return sceneState;
     55 };
     56 export default function OilArtCanvasWrapper({ onComplete }: StageProps) {
     57   const apiRef = useRef<OilArtAPI>({} as OilArtAPI);
     58   const sceneState = useSequentialAudio(1, AUDIO_SOURCES, onComplete);
     59   useEffect(() => {
     60     const intervalId = setInterval(() => {
     61       if (apiRef.current.dropOil) {
     62         const x = Math.random() * 2 - 1; // -1.0〜1.0
     63         const y = Math.random() * 2 - 1; // -1.0〜1.0
     64         const color = new THREE.Color(
     65           Math.random(),
     66           Math.random(),
     67           Math.random(),
     68         );
     69         apiRef.current.dropOil({ x, y }, color);
     70         const tiltX = Math.random() * 0.8 - 0.4;
     71         const tiltY = Math.random() * 0.8 - 0.4;
     72         apiRef.current.tilt({ x: tiltX, y: tiltY });
     73       }
     74     }, 1500);
     75     return () => clearInterval(intervalId);
     76   }, []);
     77   return (
     78     <Canvas camera={{ position: [0, 0, 1] }}>
     79       <color attach="background" args={[0x000000]} />
     80       <OilArtPlane onTriggerAPI={(api) => (apiRef.current = api)} />
     81       <OrbitControls
     82         enableZoom={false}
     83         enablePan={false}
     84         enableRotate={false}
     85       />
     86     </Canvas>
     87   );
     88 }