Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions apps/game/src/components/Screens/VSAnimation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@ export const VSAnimation: FC<BaseScreenProps> = ({ send, className, ...props })
`${playerBillionaire?.name} versus ${cpuBillionaire?.name} animation`,
);

// Call load() to ensure Safari re-processes the video
preloadedVideo.load();

// Listen for video end event to trigger state machine transition
const handleVideoEnd = () => {
send?.({ type: 'VS_ANIMATION_COMPLETE' });
Expand Down
68 changes: 57 additions & 11 deletions apps/game/src/components/SpecialCardAnimation/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { TRACKS } from '@/config/audio-config';
import { useGameStore } from '@/store';
import { cn } from '@/utils/cn';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useState } from 'react';
import type { SpecialCardAnimationProps } from './types';
import { getPreloadedVideo } from '@/hooks/use-video-preloader';

/**
* Generic Special Card Animation Component
Expand All @@ -21,9 +22,48 @@ export const SpecialCardAnimation = ({
removeBlur = false,
audioTrack,
}: SpecialCardAnimationProps) => {
const [isPreloaded, setIsPreloaded] = useState(false);
const { playAudio } = useGameStore();
const videoContainerRef = useRef<HTMLDivElement>(null);
const videoRef = useRef<HTMLVideoElement>(null);
const audioPlayedRef = useRef(false);
const videoClasses = cn('absolute inset-0 w-full h-full object-cover', videoClassName);
const videoTitle = title ? `${title} animation` : 'Special card animation';

// Check for preloaded video and use it if available
useEffect(() => {
const preloadedVideo = getPreloadedVideo(videoSrc);
const container = videoContainerRef.current;

if (!container) return;

if (preloadedVideo) {
preloadedVideo.className = videoClasses;
preloadedVideo.setAttribute('aria-label', videoTitle);
preloadedVideo.loop = loop;
preloadedVideo.controls = controls;
preloadedVideo.muted = true;

if (preloadedVideo.parentNode !== container) {
container.appendChild(preloadedVideo);
}

videoRef.current = preloadedVideo;

setIsPreloaded(true);
} else {
setIsPreloaded(false);
videoRef.current = null;
}

return () => {
// remove preloaded video on unmount from container and reset classes
if (preloadedVideo && preloadedVideo.parentNode === container) {
container.removeChild(preloadedVideo);
preloadedVideo.className = '';
}
};
}, [controls, loop, videoClasses, videoSrc, videoTitle]);

// Auto-play video when component becomes visible
useEffect(() => {
Expand Down Expand Up @@ -53,16 +93,22 @@ export const SpecialCardAnimation = ({
}`}
>
{/* Video fills full board */}
<video
ref={videoRef}
src={videoSrc}
loop={loop}
muted
playsInline
controls={controls}
className={`absolute inset-0 w-full h-full object-cover ${videoClassName}`}
aria-label={title ? `${title} animation` : 'Special card animation'}
/>
{isPreloaded ? (
// Container used for attaching the preloaded video in the useEffect
<div ref={videoContainerRef} className="absolute inset-0" />
) : (
// No preloaded video, fallback to video element
<video
ref={videoRef}
src={videoSrc}
loop={loop}
muted
playsInline
controls={controls}
className={videoClasses}
aria-label={videoTitle}
/>
)}
</div>
</div>
);
Expand Down
Loading