Files
JDClone/jd-clone/src/components/game/VideoPlayer.tsx

168 lines
4.0 KiB
TypeScript

import { useEffect, useRef } from 'react';
import useAppStore from '../../store/app-store';
import './VideoPlayer.scss';
interface VideoPlayerProps {
src: string;
audioSrc?: string;
playing: boolean;
muted?: boolean;
width?: number | string;
height?: number | string;
onEnded?: () => void;
}
const VideoPlayer = ({
src,
audioSrc,
playing,
muted = false,
width = '100%',
height = '100%',
onEnded
}: VideoPlayerProps) => {
const videoRef = useRef<HTMLVideoElement | null>(null);
const audioRef = useRef<HTMLAudioElement | null>(null);
const masterVolume = useAppStore(state => state.settings.volume.master);
const musicVolume = useAppStore(state => state.settings.volume.music);
// Manage play/pause state for video
useEffect(() => {
const video = videoRef.current;
if (!video) return;
if (playing) {
video.play().catch(error => {
console.warn('Video playback prevented:', error);
});
} else {
video.pause();
}
}, [playing]);
// Manage play/pause state for audio
useEffect(() => {
const audio = audioRef.current;
if (!audio || !audioSrc) return;
if (playing) {
// Sync with video if needed
if (videoRef.current) {
audio.currentTime = videoRef.current.currentTime;
}
audio.play().catch(error => {
console.warn('Audio playback prevented:', error);
});
} else {
audio.pause();
}
}, [playing, audioSrc]);
// Sync audio with video when video seeks or loads
useEffect(() => {
const video = videoRef.current;
const audio = audioRef.current;
if (!video || !audio || !audioSrc) return;
const handleTimeUpdate = () => {
// Only sync if the difference is significant (more than 0.1 seconds)
if (Math.abs(video.currentTime - audio.currentTime) > 0.1) {
audio.currentTime = video.currentTime;
}
};
const handlePlay = () => {
if (playing) {
audio.play().catch(error => {
console.warn('Audio playback prevented:', error);
});
}
};
const handlePause = () => {
audio.pause();
};
video.addEventListener('seeked', handleTimeUpdate);
video.addEventListener('play', handlePlay);
video.addEventListener('pause', handlePause);
return () => {
video.removeEventListener('seeked', handleTimeUpdate);
video.removeEventListener('play', handlePlay);
video.removeEventListener('pause', handlePause);
};
}, [playing, audioSrc]);
// Manage volume for video
useEffect(() => {
const video = videoRef.current;
if (!video) return;
// If we have a separate audio source, mute the video
if (audioSrc) {
video.muted = true;
} else {
// Otherwise use the video's audio
if (muted) {
video.muted = true;
} else {
video.muted = false;
video.volume = masterVolume * musicVolume;
}
}
}, [masterVolume, musicVolume, muted, audioSrc]);
// Manage volume for audio
useEffect(() => {
const audio = audioRef.current;
if (!audio || !audioSrc) return;
if (muted) {
audio.muted = true;
} else {
audio.muted = false;
audio.volume = masterVolume * musicVolume;
}
}, [masterVolume, musicVolume, muted, audioSrc]);
// Setup event handlers for video
useEffect(() => {
const video = videoRef.current;
if (!video) return;
const handleEnded = () => {
if (onEnded) onEnded();
};
video.addEventListener('ended', handleEnded);
return () => {
video.removeEventListener('ended', handleEnded);
};
}, [onEnded]);
return (
<div className="video-player" style={{ width, height }}>
<video
ref={videoRef}
src={src}
preload="auto"
playsInline
width="100%"
height="100%"
muted={!!audioSrc || muted} // Always mute video if using separate audio
/>
{audioSrc && (
<audio
ref={audioRef}
src={audioSrc}
preload="auto"
/>
)}
</div>
);
};
export default VideoPlayer;