improved background video behavior - when loop disabled the video plays once and stops in the last frame
This commit is contained in:
parent
b66bced6ab
commit
fe82dbb318
@ -46,7 +46,7 @@ const CanvasBackground: React.FC<CanvasBackgroundProps> = ({
|
|||||||
videoEndTime = null,
|
videoEndTime = null,
|
||||||
}) => {
|
}) => {
|
||||||
// Use background video playback hook for custom start/end time handling
|
// Use background video playback hook for custom start/end time handling
|
||||||
const { videoRef } = useBackgroundVideoPlayback({
|
const { videoRef, shouldBlockAutoplay } = useBackgroundVideoPlayback({
|
||||||
videoUrl: backgroundVideoUrl,
|
videoUrl: backgroundVideoUrl,
|
||||||
autoplay: videoAutoplay,
|
autoplay: videoAutoplay,
|
||||||
loop: videoLoop,
|
loop: videoLoop,
|
||||||
@ -55,6 +55,9 @@ const CanvasBackground: React.FC<CanvasBackgroundProps> = ({
|
|||||||
endTime: videoEndTime,
|
endTime: videoEndTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Block autoplay if video already played this session (when loop=false)
|
||||||
|
const effectiveAutoplay = videoAutoplay && !shouldBlockAutoplay;
|
||||||
|
|
||||||
const handleLoad = () => {
|
const handleLoad = () => {
|
||||||
onBackgroundReady?.();
|
onBackgroundReady?.();
|
||||||
};
|
};
|
||||||
@ -117,7 +120,7 @@ const CanvasBackground: React.FC<CanvasBackgroundProps> = ({
|
|||||||
key={`bg_video_${backgroundVideoUrl}`}
|
key={`bg_video_${backgroundVideoUrl}`}
|
||||||
className='absolute inset-0 z-1 h-full w-full object-contain'
|
className='absolute inset-0 z-1 h-full w-full object-contain'
|
||||||
src={backgroundVideoUrl}
|
src={backgroundVideoUrl}
|
||||||
autoPlay={videoAutoplay}
|
autoPlay={effectiveAutoplay}
|
||||||
loop={useNativeLoop}
|
loop={useNativeLoop}
|
||||||
muted={videoMuted}
|
muted={videoMuted}
|
||||||
playsInline
|
playsInline
|
||||||
|
|||||||
@ -3,11 +3,18 @@
|
|||||||
*
|
*
|
||||||
* Manages background video playback with custom start/end times.
|
* Manages background video playback with custom start/end times.
|
||||||
* Follows patterns from useTransitionPlayback for video time control.
|
* Follows patterns from useTransitionPlayback for video time control.
|
||||||
|
*
|
||||||
|
* When loop is disabled, videos are tracked per-session so they only play once
|
||||||
|
* and show the last frame on subsequent page visits (until browser refresh).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useRef, useCallback, type RefObject } from 'react';
|
import { useEffect, useRef, useCallback, type RefObject } from 'react';
|
||||||
import { logger } from '../lib/logger';
|
import { logger } from '../lib/logger';
|
||||||
|
|
||||||
|
// Session-scoped tracking of videos that have finished playing (when loop=false)
|
||||||
|
// Key: videoUrl, cleared on browser refresh
|
||||||
|
const playedVideos = new Set<string>();
|
||||||
|
|
||||||
export interface UseBackgroundVideoPlaybackOptions {
|
export interface UseBackgroundVideoPlaybackOptions {
|
||||||
/** URL of the video to play */
|
/** URL of the video to play */
|
||||||
videoUrl?: string;
|
videoUrl?: string;
|
||||||
@ -26,6 +33,8 @@ export interface UseBackgroundVideoPlaybackOptions {
|
|||||||
export interface UseBackgroundVideoPlaybackResult {
|
export interface UseBackgroundVideoPlaybackResult {
|
||||||
/** Ref to attach to the video element */
|
/** Ref to attach to the video element */
|
||||||
videoRef: RefObject<HTMLVideoElement | null>;
|
videoRef: RefObject<HTMLVideoElement | null>;
|
||||||
|
/** Whether autoplay should be blocked (video already played this session) */
|
||||||
|
shouldBlockAutoplay: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,6 +65,9 @@ export function useBackgroundVideoPlayback({
|
|||||||
endTime = null,
|
endTime = null,
|
||||||
}: UseBackgroundVideoPlaybackOptions): UseBackgroundVideoPlaybackResult {
|
}: UseBackgroundVideoPlaybackOptions): UseBackgroundVideoPlaybackResult {
|
||||||
const videoRef = useRef<HTMLVideoElement | null>(null);
|
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||||
|
|
||||||
|
// Block autoplay if video already played this session (only when loop=false)
|
||||||
|
const shouldBlockAutoplay = !loop && videoUrl ? playedVideos.has(videoUrl) : false;
|
||||||
// Store current values in refs for event handlers to access
|
// Store current values in refs for event handlers to access
|
||||||
const startTimeRef = useRef(startTime);
|
const startTimeRef = useRef(startTime);
|
||||||
const endTimeRef = useRef(endTime);
|
const endTimeRef = useRef(endTime);
|
||||||
@ -174,7 +186,35 @@ export function useBackgroundVideoPlayback({
|
|||||||
video.muted = muted;
|
video.muted = muted;
|
||||||
}, [muted]);
|
}, [muted]);
|
||||||
|
|
||||||
return { videoRef };
|
// Session-scoped "play once" behavior when loop is disabled
|
||||||
|
// Videos that have already played show last frame on revisit
|
||||||
|
useEffect(() => {
|
||||||
|
const video = videoRef.current;
|
||||||
|
if (!video || !videoUrl || loop) return;
|
||||||
|
|
||||||
|
// If video already played this session, show last frame
|
||||||
|
if (playedVideos.has(videoUrl)) {
|
||||||
|
const showLastFrame = () => {
|
||||||
|
video.currentTime = video.duration - 0.01;
|
||||||
|
video.pause();
|
||||||
|
};
|
||||||
|
if (video.readyState >= 1) {
|
||||||
|
showLastFrame();
|
||||||
|
} else {
|
||||||
|
video.addEventListener('loadedmetadata', showLastFrame, { once: true });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark video as played when it ends
|
||||||
|
const handleEnded = () => {
|
||||||
|
playedVideos.add(videoUrl);
|
||||||
|
};
|
||||||
|
video.addEventListener('ended', handleEnded);
|
||||||
|
return () => video.removeEventListener('ended', handleEnded);
|
||||||
|
}, [videoUrl, loop]);
|
||||||
|
|
||||||
|
return { videoRef, shouldBlockAutoplay };
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useBackgroundVideoPlayback;
|
export default useBackgroundVideoPlayback;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user