145 lines
4.2 KiB
TypeScript
145 lines
4.2 KiB
TypeScript
/**
|
|
* CanvasBackground Component
|
|
*
|
|
* Background image, video, and audio for the constructor canvas.
|
|
* Handles blob URLs, Next.js Image optimization, and previous background overlay.
|
|
* Supports custom video playback settings (autoplay, loop, muted, start/end time).
|
|
*/
|
|
|
|
import React from 'react';
|
|
import NextImage from 'next/image';
|
|
import { useBackgroundVideoPlayback } from '../../hooks/useBackgroundVideoPlayback';
|
|
import PreviousBackgroundOverlay from '../PreviousBackgroundOverlay';
|
|
|
|
interface CanvasBackgroundProps {
|
|
backgroundImageUrl?: string;
|
|
backgroundVideoUrl?: string;
|
|
backgroundAudioUrl?: string;
|
|
previousBgImageUrl?: string;
|
|
previousBgVideoUrl?: string;
|
|
isSwitching?: boolean;
|
|
isNewBgReady?: boolean;
|
|
isFadingIn?: boolean;
|
|
onBackgroundReady?: () => void;
|
|
// Video playback settings
|
|
videoAutoplay?: boolean;
|
|
videoLoop?: boolean;
|
|
videoMuted?: boolean;
|
|
videoStartTime?: number | null;
|
|
videoEndTime?: number | null;
|
|
}
|
|
|
|
const CanvasBackground: React.FC<CanvasBackgroundProps> = ({
|
|
backgroundImageUrl,
|
|
backgroundVideoUrl,
|
|
backgroundAudioUrl,
|
|
previousBgImageUrl,
|
|
previousBgVideoUrl,
|
|
isSwitching = false,
|
|
isNewBgReady = false,
|
|
isFadingIn = false,
|
|
onBackgroundReady,
|
|
videoAutoplay = true,
|
|
videoLoop = true,
|
|
videoMuted = true,
|
|
videoStartTime = null,
|
|
videoEndTime = null,
|
|
}) => {
|
|
// Use background video playback hook for custom start/end time handling
|
|
const { videoRef, shouldBlockAutoplay } = useBackgroundVideoPlayback({
|
|
videoUrl: backgroundVideoUrl,
|
|
autoplay: videoAutoplay,
|
|
loop: videoLoop,
|
|
muted: videoMuted,
|
|
startTime: videoStartTime,
|
|
endTime: videoEndTime,
|
|
});
|
|
|
|
// Block autoplay if video already played this session (when loop=false)
|
|
const effectiveAutoplay = videoAutoplay && !shouldBlockAutoplay;
|
|
|
|
const handleLoad = () => {
|
|
onBackgroundReady?.();
|
|
};
|
|
|
|
const handleError = () => {
|
|
onBackgroundReady?.();
|
|
};
|
|
|
|
// When endTime is set, we disable native loop and handle it via the hook
|
|
const useNativeLoop = videoEndTime == null ? videoLoop : false;
|
|
|
|
return (
|
|
<>
|
|
{/* Background image - z-1 keeps it below backdrop blur layer (z-5) */}
|
|
{backgroundImageUrl && (
|
|
<div className='pointer-events-none absolute inset-0 z-1 h-full w-full select-none'>
|
|
{backgroundImageUrl.startsWith('blob:') ? (
|
|
// eslint-disable-next-line @next/next/no-img-element
|
|
<img
|
|
key={`bg_image_${backgroundImageUrl}`}
|
|
src={backgroundImageUrl}
|
|
alt='Background'
|
|
className='absolute inset-0 h-full w-full object-contain'
|
|
draggable={false}
|
|
onLoad={handleLoad}
|
|
onError={handleError}
|
|
/>
|
|
) : (
|
|
<NextImage
|
|
key={`bg_image_${backgroundImageUrl}`}
|
|
src={backgroundImageUrl}
|
|
alt='Background'
|
|
fill
|
|
sizes='100vw'
|
|
className='object-contain'
|
|
draggable={false}
|
|
unoptimized
|
|
onLoad={handleLoad}
|
|
onError={handleError}
|
|
/>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Previous background overlays - show during loading AND crossfade.
|
|
Uses CSS animation for fade-out effect during crossfade.
|
|
z-0 keeps them BELOW new backgrounds (z-1). */}
|
|
<PreviousBackgroundOverlay
|
|
imageUrl={previousBgImageUrl}
|
|
videoUrl={previousBgVideoUrl}
|
|
isSwitching={isSwitching}
|
|
isNewBgReady={isNewBgReady}
|
|
isFadingIn={isFadingIn}
|
|
/>
|
|
|
|
{/* Background video - z-1 keeps it below backdrop blur layer (z-5) */}
|
|
{backgroundVideoUrl && (
|
|
<video
|
|
ref={videoRef}
|
|
key={`bg_video_${backgroundVideoUrl}`}
|
|
className='absolute inset-0 z-1 h-full w-full object-contain'
|
|
src={backgroundVideoUrl}
|
|
autoPlay={effectiveAutoplay}
|
|
loop={useNativeLoop}
|
|
muted={videoMuted}
|
|
playsInline
|
|
/>
|
|
)}
|
|
|
|
{/* Background audio */}
|
|
{backgroundAudioUrl && (
|
|
<audio
|
|
key={`bg_audio_${backgroundAudioUrl}`}
|
|
src={backgroundAudioUrl}
|
|
autoPlay
|
|
loop
|
|
hidden
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default CanvasBackground;
|