Fix image fallback abrupt segue, add logging

This commit is contained in:
Sam Becker 2025-06-23 23:19:22 -05:00
parent a1886da3bc
commit 6d7c5d6903

View File

@ -29,21 +29,45 @@ export default function ImageWithFallback({
const [hideFallback, setHideFallback] = useState(false); const [hideFallback, setHideFallback] = useState(false);
const imgRef = useRef<HTMLImageElement>(null); const refImage = useRef<HTMLImageElement>(null);
const refFallback = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
setWasCached( setWasCached(
Boolean(imgRef.current?.complete) && Boolean(refImage.current?.complete) &&
(imgRef.current?.naturalWidth ?? 0) > 0, (refImage.current?.naturalWidth ?? 0) > 0,
); );
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!isLoading && !didError) { if (!isLoading && !didError) {
let innerTimeout: NodeJS.Timeout;
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
setHideFallback(true); if (refFallback.current) {
const fallbackOpacity = (refFallback
.current
.computedStyleMap()
.get('opacity') as CSSUnitValue
)?.value;
// Address race condition where cached image is initially loaded
// and fallback is still being shown at full opacity
if (fallbackOpacity === 0) {
// Image has loaded and fallback is already hidden
setHideFallback(true);
} else {
// Image has loaded but fallback is still visible
// Delay hiding fallback to avoid abrupt transition
innerTimeout = setTimeout(() =>{
console.log('Delayed hide fallback');
setHideFallback(true);
}, 1000);
}
}
}, 1000); }, 1000);
return () => clearTimeout(timeout); return () => {
clearTimeout(timeout);
clearTimeout(innerTimeout);
};
} }
}, [isLoading, didError]); }, [isLoading, didError]);
@ -70,23 +94,26 @@ export default function ImageWithFallback({
> >
<Image {...{ <Image {...{
...props, ...props,
ref: imgRef, ref: refImage,
priority, priority,
className: classNameImage, className: classNameImage,
onLoad, onLoad,
onError, onError,
}} /> }} />
<div className={clsx( <div
'@container', ref={refFallback}
'absolute inset-0 pointer-events-none', className={clsx(
'overflow-hidden', '@container',
(showFallback || shouldDebugImageFallbacks) && 'absolute inset-0 pointer-events-none',
'transition-opacity duration-300 ease-in', 'overflow-hidden',
!(BLUR_ENABLED && blurDataURL) && 'bg-main', (showFallback || shouldDebugImageFallbacks) &&
(isLoading || shouldDebugImageFallbacks) 'transition-opacity duration-300 ease-in',
? 'opacity-100' !(BLUR_ENABLED && blurDataURL) && 'bg-main',
: 'opacity-0', (isLoading || shouldDebugImageFallbacks)
)}> ? 'opacity-100'
: 'opacity-0',
)}
>
{(BLUR_ENABLED && blurDataURL) {(BLUR_ENABLED && blurDataURL)
? <img {...{ ? <img {...{
...props, ...props,