1
- import { useEffect , useRef , useState } from 'react' ;
1
+ import { useEffect , useMemo , useRef , useState } from 'react' ;
2
2
3
3
import PhotoSwipeLightbox from 'photoswipe/lightbox' ;
4
4
@@ -8,11 +8,14 @@ import type { FC } from 'react';
8
8
import 'photoswipe/style.css' ;
9
9
10
10
import { GALLERY_ID } from '@/constants/gallery' ;
11
+ import { cn } from '@/utils/styles' ;
11
12
12
13
interface Props {
13
14
images : ImageProps [ ] ;
14
15
}
15
16
17
+ type LoadedStates = Record < string , boolean > ;
18
+
16
19
// step
17
20
const PAGE_SIZE = 3 as const ; // Todo: make it responsive
18
21
// page dependency in useEffect is more important
@@ -28,6 +31,14 @@ const NewGallery: FC<Props> = ({ images }) => {
28
31
const [ page , setPage ] = useState < number > ( INITIAL_PAGE ) ;
29
32
const observerTarget = useRef ( null ) ;
30
33
34
+ const [ loadedStates , setLoadedStates ] = useState < LoadedStates > ( { } ) ;
35
+ // calculate if new page is loaded on scroll
36
+ // not for blur transition
37
+ const isAllImagesLoaded = useMemo (
38
+ ( ) => Object . values ( loadedStates ) . every ( Boolean ) ,
39
+ [ loadedStates , loadedImages . length ]
40
+ ) ;
41
+
31
42
const isEnd = loadedImages . length === images . length ;
32
43
33
44
// converts page to loaded images
@@ -39,8 +50,8 @@ const NewGallery: FC<Props> = ({ images }) => {
39
50
// sets only page
40
51
useEffect ( ( ) => {
41
52
const callback : IntersectionObserverCallback = ( entries ) => {
42
- // Todo: must wait here for images to load
43
- if ( ! isEnd && entries [ 0 ] . isIntersecting ) {
53
+ // must wait here for images to load
54
+ if ( ! isEnd && isAllImagesLoaded && entries [ 0 ] . isIntersecting ) {
44
55
setPage ( ( prevPage ) => prevPage + 1 ) ;
45
56
}
46
57
} ;
@@ -72,6 +83,10 @@ const NewGallery: FC<Props> = ({ images }) => {
72
83
} ;
73
84
} , [ ] ) ;
74
85
86
+ const handleLoad = ( src : string ) => {
87
+ setLoadedStates ( ( prev ) => ( { ...prev , [ src ] : true } ) ) ;
88
+ } ;
89
+
75
90
return (
76
91
< >
77
92
< div
@@ -87,7 +102,17 @@ const NewGallery: FC<Props> = ({ images }) => {
87
102
target = "_blank"
88
103
rel = "noreferrer"
89
104
>
90
- < img src = { image . xs . src } alt = "Gallery image" className = "w-full" />
105
+ < img
106
+ src = { image . xs . src }
107
+ onLoad = { ( ) => handleLoad ( image . xs . src ) }
108
+ alt = "Gallery image"
109
+ className = { cn (
110
+ 'w-full transition-all duration-[2s] ease-in-out' ,
111
+ loadedStates [ image . xs . src ]
112
+ ? 'opacity-100 blur-0 grayscale-0'
113
+ : 'opacity-75 blur-sm grayscale'
114
+ ) }
115
+ />
91
116
</ a >
92
117
) ) }
93
118
</ div >
0 commit comments