From 8ea82e8c09335b818873fbcd9c5ad222d0f1a35f Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Tue, 24 Jun 2025 20:58:07 -0500 Subject: [PATCH] Give cached fallback images 200ms to load --- src/components/image/ImageWithFallback.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/image/ImageWithFallback.tsx b/src/components/image/ImageWithFallback.tsx index 37dd5437..407b18cf 100644 --- a/src/components/image/ImageWithFallback.tsx +++ b/src/components/image/ImageWithFallback.tsx @@ -7,6 +7,9 @@ import { clsx} from 'clsx/lite'; import Image, { ImageProps } from 'next/image'; import { useCallback, useEffect, useRef, useState } from 'react'; +// If image is still loading after 200ms, force CSS animation +const FALLBACK_FADE_CUTOFF = 200; + export default function ImageWithFallback({ className, classNameImage = 'object-cover h-full', @@ -31,6 +34,7 @@ export default function ImageWithFallback({ const onLoad = useCallback(() => setIsLoading(false), []); const onError = useCallback(() => setDidError(true), []); + const [forceFallbackFade, setForceFallbackFade] = useState(false); const [hideFallback, setHideFallback] = useState(false); const refImage = useRef(null); @@ -50,16 +54,15 @@ export default function ImageWithFallback({ isLoadingRef.current = isLoading; }, [isLoading]); - const forceTransition = useRef(false); useEffect(() => { const timeout = setTimeout(() => { // If image is still loading, force CSS animation if (isLoadingRef.current) { - forceTransition.current = true; + setForceFallbackFade(true); } - }, 200); + }, FALLBACK_FADE_CUTOFF); return () => clearTimeout(timeout); - }, [shouldDebugFallbackTiming]); + }, []); const timeStartRef = useRef(performance.now()); useEffect(() => { @@ -81,7 +84,7 @@ export default function ImageWithFallback({ wasCached: wasCachedRef.current, hideFallback, showFallback, - forceTransition: forceTransition.current, + forceTransition: forceFallbackFade, }); } @@ -174,7 +177,7 @@ export default function ImageWithFallback({ '@container', 'absolute inset-0 pointer-events-none', 'overflow-hidden', - (showFallback || shouldDebugImageFallbacks) && + ((showFallback && !forceFallbackFade) || shouldDebugImageFallbacks) && 'transition-opacity duration-300 ease-in', !(BLUR_ENABLED && blurDataURL) && 'bg-main', (isLoading || shouldDebugImageFallbacks)