1
1
import {
2
- useState ,
3
- useEffect ,
4
- useRef ,
5
- useContext ,
6
- useMemo ,
7
2
type CSSProperties ,
3
+ type FC ,
8
4
type KeyboardEvent ,
9
5
type ReactNode ,
10
- type FC ,
6
+ useContext ,
7
+ useEffect ,
8
+ useLayoutEffect ,
9
+ useMemo ,
10
+ useRef ,
11
+ useState ,
12
+ useTransition ,
11
13
} from "react" ;
12
14
import { motion , type MotionProps } from "framer-motion" ;
13
15
@@ -25,6 +27,7 @@ export interface FPContainerInterface {
25
27
onHide ?: Function ;
26
28
onShow ?: Function ;
27
29
outerStyle ?: CSSProperties ;
30
+ scrollDebounceMs ?: number ;
28
31
style ?: CSSProperties ;
29
32
transitionTiming ?: number ;
30
33
}
@@ -38,12 +41,17 @@ export const FPContainer: FC<FPContainerInterface> = ({
38
41
onHide,
39
42
onShow,
40
43
outerStyle = { } ,
44
+ scrollDebounceMs = 125 ,
41
45
style = { } ,
42
46
transitionTiming = 700 ,
43
47
} ) => {
44
- const throttled = useRef ( false ) ;
45
- const ticking = useRef ( false ) ;
46
- const scrollY = useRef ( isSsr ? 0 : window . scrollY ) ;
48
+ const FPContainerInnerRef = useRef < HTMLDivElement > ( null ) ;
49
+ const scrollTimer = useRef < Timer > ( null ) ;
50
+ // @see https://developer.mozilla.org/en-US/docs/Web/API/Window/pageYOffset
51
+ // ^ IE users dont deserve our support
52
+ const scrollY = useRef < number > ( isSsr ? 0 : window . scrollY ) ;
53
+ const throttled = useRef < boolean > ( false ) ;
54
+ const ticking = useRef < boolean > ( false ) ;
47
55
48
56
const useOuterStyle = useMemo (
49
57
( ) => ( {
@@ -66,9 +74,9 @@ export const FPContainer: FC<FPContainerInterface> = ({
66
74
[ style ]
67
75
) ;
68
76
69
- const FPContainerInnerRef = useRef < HTMLDivElement > ( null ) ;
77
+ const { ReactFPRef , slides , isFullscreen } = useContext ( FPContext ) ;
70
78
71
- const { ReactFPRef , slides } = useContext ( FPContext ) ;
79
+ const [ , startTransition ] = useTransition ( ) ;
72
80
73
81
const [ pageState , setPageState ] = useState ( {
74
82
fullpageHeight : 0 ,
@@ -81,39 +89,49 @@ export const FPContainer: FC<FPContainerInterface> = ({
81
89
} ) ;
82
90
83
91
// @see https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event
84
- const handleScroll = ( e : UIEvent ) => {
92
+ const handleScroll = ( ) => {
85
93
if ( throttled . current || isSsr ) return ;
86
94
throttled . current = true ;
87
95
88
- e . stopPropagation ( ) ;
89
-
90
- // scroll first, then enable throttling
91
- setTimeout ( ( ) => {
92
- throttled . current = false ;
93
- } , transitionTiming ) ;
94
-
95
96
if ( ! ticking . current ) {
96
97
window . requestAnimationFrame ( ( ) => {
97
98
const newScrollY = window . scrollY ;
98
99
const prevScrollY = scrollY . current ;
99
100
100
- if ( prevScrollY < newScrollY ) forward ( ) ;
101
+ if ( newScrollY === 0 ) first ( ) ;
102
+ else if (
103
+ window . innerHeight + Math . round ( newScrollY ) >=
104
+ document . body . offsetHeight
105
+ )
106
+ last ( ) ;
107
+ else if ( prevScrollY < newScrollY ) forward ( ) ;
101
108
else if ( prevScrollY > newScrollY ) back ( ) ;
102
109
103
110
if (
104
111
pageState . resetScroll ||
105
112
transitionTiming !== pageState . transitionTiming
106
113
)
107
- setPageState ( ( prevState ) => ( {
108
- ...prevState ,
109
- resetScroll : false ,
110
- transitionTiming,
111
- } ) ) ;
114
+ startTransition ( ( ) => {
115
+ setPageState ( ( prevState ) => ( {
116
+ ...prevState ,
117
+ resetScroll : false ,
118
+ transitionTiming,
119
+ } ) ) ;
120
+ } ) ;
112
121
113
122
ticking . current = false ;
114
123
} ) ;
115
124
ticking . current = true ;
116
125
}
126
+
127
+ setTimeout ( ( ) => {
128
+ throttled . current = false ;
129
+ } , transitionTiming ) ;
130
+ } ;
131
+
132
+ const bouncedHandleScroll = ( ) => {
133
+ clearTimeout ( scrollTimer . current ) ;
134
+ scrollTimer . current = setTimeout ( ( ) => handleScroll ( ) , scrollDebounceMs ) ;
117
135
} ;
118
136
119
137
const handleResize = ( ) => {
@@ -134,15 +152,17 @@ export const FPContainer: FC<FPContainerInterface> = ({
134
152
if ( ! ticking . current ) {
135
153
requestAnimationFrame ( ( ) => {
136
154
const fullpageHeight = FPContainerInnerRef . current ! . clientHeight ;
137
- // update count
138
- setPageState ( ( prevState ) => ( {
139
- ...prevState ,
140
- fullpageHeight,
141
- viewportHeight : Math . max (
142
- document . documentElement . clientHeight ,
143
- window . innerHeight
144
- ) ,
145
- } ) ) ;
155
+
156
+ startTransition ( ( ) => {
157
+ setPageState ( ( prevState ) => ( {
158
+ ...prevState ,
159
+ fullpageHeight,
160
+ viewportHeight : Math . max (
161
+ document . documentElement . clientHeight ,
162
+ window . innerHeight
163
+ ) ,
164
+ } ) ) ;
165
+ } ) ;
146
166
ReactFPRef . current ! . style . height = `${ fullpageHeight } px` ;
147
167
ticking . current = false ;
148
168
} ) ;
@@ -203,7 +223,10 @@ export const FPContainer: FC<FPContainerInterface> = ({
203
223
slideIndex,
204
224
translateY,
205
225
} ;
206
- setPageState ( ( prevState ) => ( { ...prevState , ...newPageState } ) ) ;
226
+
227
+ startTransition ( ( ) => {
228
+ setPageState ( ( prevState ) => ( { ...prevState , ...newPageState } ) ) ;
229
+ } ) ;
207
230
208
231
setTimeout ( ( ) => {
209
232
throttled . current = false ;
@@ -256,20 +279,21 @@ export const FPContainer: FC<FPContainerInterface> = ({
256
279
useEffect ( ( ) => {
257
280
if ( isSsr ) return ;
258
281
259
- window . addEventListener ( "scroll" , handleScroll , { passive : true } ) ;
282
+ window . addEventListener ( "scroll" , bouncedHandleScroll , { passive : true } ) ;
260
283
window . addEventListener ( "resize" , handleResize , { passive : true } ) ;
261
284
document . addEventListener ( "keydown" , handleKeys , { passive : true } ) ;
262
285
263
286
return ( ) => {
264
- window . removeEventListener ( "scroll" , handleScroll ) ;
287
+ window . removeEventListener ( "scroll" , bouncedHandleScroll ) ;
265
288
window . removeEventListener ( "resize" , handleResize ) ;
266
289
document . removeEventListener ( "keydown" , handleKeys ) ;
267
290
} ;
268
291
} ) ;
269
292
270
- useEffect ( ( ) => {
293
+ useLayoutEffect ( ( ) => {
271
294
handleResize ( ) ;
272
- } ) ;
295
+ handleScroll ( ) ;
296
+ } , [ isFullscreen ] ) ;
273
297
274
298
return (
275
299
< div style = { useOuterStyle } >
0 commit comments