UI: Hardware-aware video reveal with click-to-play bridge for iPhone XR
This commit is contained in:
parent
3c3f803a1c
commit
d166e92f8c
@ -287,44 +287,62 @@ export function UserHeader({ user, preferences, onModalStateChange }: UserHeader
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Static Poster Image Layer (Safe Fallback) */}
|
||||
<img
|
||||
src="/videos/smoke-poster.jpg"
|
||||
alt=""
|
||||
className={cn(
|
||||
"absolute inset-0 w-full h-full object-cover scale-110 transition-opacity duration-1000",
|
||||
isVideoPlaying ? "opacity-30" : "opacity-70 dark:opacity-50"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
{/* Smoke Video background - Only visible when MOVING */}
|
||||
<video
|
||||
ref={videoRef}
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
{...({
|
||||
"webkit-playsinline": "true",
|
||||
"x-webkit-airplay": "deny",
|
||||
"disableRemotePlayback": true
|
||||
} as any)}
|
||||
preload="auto"
|
||||
controls={false}
|
||||
disablePictureInPicture
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
onLoadedData={() => setIsVideoLoaded(true)}
|
||||
onPlay={() => setIsVideoPlaying(true)}
|
||||
className={cn(
|
||||
"absolute inset-0 w-full h-full object-cover scale-110 transition-opacity duration-[1500ms] ease-in-out",
|
||||
isVideoPlaying
|
||||
? "opacity-70 dark:opacity-50"
|
||||
: "opacity-0"
|
||||
)}
|
||||
{/* Wrapper for background image/video to allow click-to-play */}
|
||||
<div
|
||||
className="absolute inset-0 w-full h-full pointer-events-auto"
|
||||
onClick={() => {
|
||||
if (videoRef.current && videoRef.current.paused) {
|
||||
videoRef.current.play().catch((err) => {
|
||||
console.warn("User interaction play failed:", err);
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<source src="/videos/smoke.mp4" type="video/mp4" />
|
||||
</video>
|
||||
{/* Static Poster Image Layer (Safe Fallback) */}
|
||||
<img
|
||||
src="/videos/smoke-poster.jpg"
|
||||
alt=""
|
||||
className={cn(
|
||||
"absolute inset-0 w-full h-full object-cover scale-110 transition-opacity duration-1000",
|
||||
isVideoPlaying ? "opacity-30" : "opacity-70 dark:opacity-50"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
{/* Smoke Video background - Only visible when MOVING */}
|
||||
<video
|
||||
ref={videoRef}
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
{...({
|
||||
"webkit-playsinline": "true",
|
||||
"x-webkit-airplay": "deny",
|
||||
"disableRemotePlayback": true
|
||||
} as any)}
|
||||
preload="auto"
|
||||
controls={false}
|
||||
disablePictureInPicture
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
onLoadedData={() => setIsVideoLoaded(true)}
|
||||
onPlay={() => setIsVideoPlaying(true)}
|
||||
onPlaying={() => setIsVideoPlaying(true)}
|
||||
onTimeUpdate={() => {
|
||||
if (!isVideoPlaying && videoRef.current && videoRef.current.currentTime > 0) {
|
||||
setIsVideoPlaying(true);
|
||||
}
|
||||
}}
|
||||
className={cn(
|
||||
"absolute inset-0 w-full h-full object-cover scale-110 transition-opacity duration-[1500ms] ease-in-out",
|
||||
isVideoPlaying
|
||||
? "opacity-70 dark:opacity-50"
|
||||
: "opacity-[0.01]" // Keep very slightly visible so OS doesn't throttle it
|
||||
)}
|
||||
>
|
||||
<source src="/videos/smoke.mp4" type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
|
||||
{/* Vignette/Readability Overlay - Reinforced for contrast */}
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-black/30 via-transparent to-black/50 dark:from-black/60 dark:via-transparent dark:to-black/70 mix-blend-multiply" />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user