Merge pull request #60 from sambecker/blur-fade

Fade in images after load
This commit is contained in:
Sam Becker 2024-02-23 19:45:54 -06:00 committed by GitHub
commit 288e542527
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 84 additions and 41 deletions

View File

@ -1,22 +1,87 @@
'use client';
/* eslint-disable jsx-a11y/alt-text */
import { BLUR_ENABLED } from '@/site/config';
import { clsx} from 'clsx/lite';
import Image, { ImageProps } from 'next/image';
import { useEffect, useRef, useState } from 'react';
export default function ImageBlurFallback(props: ImageProps) {
const {
className,
priority,
blurDataURL,
...rest
} = props;
const [wasCached, setWasCached] = useState(true);
const [isLoading, setIsLoading] = useState(true);
const [didError, setDidError] = useState(false);
const [hideBlurPlaceholder, setHideBlurPlaceholder] = useState(false);
const imageClassName = 'object-cover h-full';
const imgRef = useRef<HTMLImageElement>(null);
useEffect(() => {
const timeout = setTimeout(
() => setWasCached(imgRef.current?.complete ?? false),
100,
);
return () => clearTimeout(timeout);
}, []);
useEffect(() => {
if (!isLoading && !didError) {
const timeout = setTimeout(() => {
setHideBlurPlaceholder(true);
}, 1000);
return () => clearTimeout(timeout);
}
}, [isLoading, didError]);
const showPlaceholder =
!wasCached &&
!hideBlurPlaceholder;
return (
// eslint-disable-next-line jsx-a11y/alt-text
<Image {...{
...props,
...BLUR_ENABLED && props.blurDataURL ? {
placeholder: 'blur',
blurDataURL: props.blurDataURL,
}: {
placeholder: 'empty',
className: clsx(
props.className,
'bg-gray-100/50 dark:bg-gray-900/50',
),
},
}} />
<div
className={clsx(
className,
'flex relative',
)}
>
{showPlaceholder &&
<div className={clsx(
'absolute inset-0',
'bg-main overflow-hidden',
'transition-opacity duration-300 ease-in',
isLoading ? 'opacity-100' : 'opacity-0',
)}>
{(BLUR_ENABLED && props.blurDataURL)
? <img {...{
...rest,
src: blurDataURL,
className: clsx(
imageClassName,
// Fix poorly blurred placeholder data generated by Safari
'blur-md scale-110',
),
}} />
: <div className={clsx(
'w-full h-full',
'bg-gray-100/50 dark:bg-gray-900/50',
)}/>}
</div>}
<Image {...{
...rest,
ref: imgRef,
priority,
className: imageClassName,
onLoad: () => setIsLoading(false),
onError: () => setDidError(true),
}} />
</div>
);
}

View File

@ -20,7 +20,6 @@ export default function ImageSmall({
src,
alt,
blurDataURL: blurData,
placeholder: 'blur',
width: IMAGE_SMALL_WIDTH,
height: Math.round(IMAGE_SMALL_WIDTH / aspectRatio),
}} />

View File

@ -19,10 +19,7 @@ export default function ImageTiny({
className,
src,
alt,
...blurData && {
blurDataURL: blurData,
placeholder: 'blur',
},
blurDataURL: blurData,
width: IMAGE_TINY_WIDTH,
height: Math.round(IMAGE_TINY_WIDTH / aspectRatio),
}} />

View File

@ -56,12 +56,7 @@ export default function PhotoGrid({
<div
key={photo.id}
className={GRID_ASPECT_RATIO !== 0
? clsx(
'aspect-square',
'overflow-hidden',
'[&>*]:flex [&>*]:w-full [&>*]:h-full',
'[&>*>*]:object-cover [&>*>*]:min-h-full',
)
? 'aspect-square overflow-hidden'
: undefined}
style={{
...GRID_ASPECT_RATIO !== 0 && {

View File

@ -5,8 +5,6 @@ import { clsx } from 'clsx/lite';
import { pathForPhoto } from '@/site/paths';
import { Camera } from '@/camera';
import { FilmSimulation } from '@/simulation';
import AdminPhotoMenu from '@/admin/AdminPhotoMenu';
import { Suspense } from 'react';
export default function PhotoSmall({
photo,
@ -14,34 +12,23 @@ export default function PhotoSmall({
camera,
simulation,
selected,
showAdminMenu,
}: {
photo: Photo
tag?: string
camera?: Camera
simulation?: FilmSimulation
selected?: boolean
showAdminMenu?: boolean
}) {
return (
<Link
href={pathForPhoto(photo, tag, camera, simulation)}
className={clsx(
'relative group',
'group',
'flex relative w-full h-full',
'active:brightness-75',
selected && 'brightness-50',
)}
>
<Suspense>
{showAdminMenu &&
<AdminPhotoMenu
buttonClassName={clsx(
'absolute top-1 right-1 opacity-0',
'group-hover:opacity-100 group-focus:opacity-100',
)}
photo={photo}
/>}
</Suspense>
<ImageSmall
src={photo.url}
aspectRatio={photo.aspectRatio}

View File

@ -5,4 +5,4 @@ export const IMAGE_TINY_WIDTH = 50;
export const IMAGE_SMALL_WIDTH = 300;
// Height determined by intrinsic photo aspect ratio
export const IMAGE_LARGE_WIDTH = 900;
export const IMAGE_LARGE_WIDTH = 1080;