Fade placeholder blurs

This commit is contained in:
Sam Becker 2024-02-23 12:06:02 -06:00
parent 25cb686ace
commit a055783c26
4 changed files with 75 additions and 35 deletions

View File

@ -1,22 +1,80 @@
'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 [hideBluePlaceholder, setHideBluePlaceholder] = 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(() => {
setHideBluePlaceholder(true);
}, 1000);
return () => clearTimeout(timeout);
}
}, [isLoading, didError]);
const showPlaceholder =
BLUR_ENABLED &&
props.blurDataURL &&
!wasCached &&
!hideBluePlaceholder;
return (
// eslint-disable-next-line jsx-a11y/alt-text
<Image {...{
...props,
...BLUR_ENABLED && props.blurDataURL ? {
placeholder: 'blur',
blurDataURL: props.blurDataURL,
}: {
<div
className={clsx(
className,
'flex relative',
'bg-gray-100/50 dark:bg-gray-900/50',
)}
>
{showPlaceholder &&
<img {...{
...rest,
src: blurDataURL,
className: clsx(
imageClassName,
'absolute',
'transition-opacity duration-300 ease-in',
isLoading ? 'opacity-100' : 'opacity-0',
),
}} />}
<Image {...{
...rest,
ref: imgRef,
priority,
className: imageClassName,
placeholder: 'empty',
className: clsx(
props.className,
'bg-gray-100/50 dark:bg-gray-900/50',
),
},
}} />
onLoad: () => setIsLoading(false),
onError: () => setDidError(true),
}} />
</div>
);
}

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;