@@ -5,6 +5,9 @@ interface UseScrollToTopOptions {
55 duration ?: number
66}
77
8+ // Easing function for the scroll animation
9+ const easeOutQuad = ( t : number ) => t * ( 2 - t )
10+
811/**
912 * Scroll to the top of the page with a smooth animation.
1013 * @param options - Configuration options for the scroll animation.
@@ -16,24 +19,11 @@ export function useScrollToTop(options: UseScrollToTopOptions) {
1619 // Check if prefers-reduced-motion is enabled
1720 const preferReducedMotion = useMediaQuery ( '(prefers-reduced-motion: reduce)' )
1821
19- // Easing function for the scroll animation
20- const easeOutQuad = ( t : number ) => t * ( 2 - t )
21-
2222 /**
2323 * Active requestAnimationFrame id for the current auto-scroll animation
2424 */
2525 let rafId : number | null = null
26- /**
27- * Disposer for temporary interaction listeners attached during auto-scroll
28- */
29- let stopInteractionListeners : ( ( ) => void ) | null = null
30-
31- function cleanupInteractionListeners ( ) {
32- if ( stopInteractionListeners ) {
33- stopInteractionListeners ( )
34- stopInteractionListeners = null
35- }
36- }
26+ const isScrolling = ref ( false )
3727
3828 /**
3929 * Stop any in-flight auto-scroll before starting a new one.
@@ -43,8 +33,21 @@ export function useScrollToTop(options: UseScrollToTopOptions) {
4333 cancelAnimationFrame ( rafId )
4434 rafId = null
4535 }
36+ isScrolling . value = false
37+ }
38+
39+ // Cancel scroll on user interaction
40+ const onInteraction = ( ) => {
41+ if ( isScrolling . value ) {
42+ cancel ( )
43+ }
44+ }
4645
47- cleanupInteractionListeners ( )
46+ if ( import . meta. client ) {
47+ const listenerOptions = { passive : true }
48+ useEventListener ( window , 'wheel' , onInteraction , listenerOptions )
49+ useEventListener ( window , 'touchstart' , onInteraction , listenerOptions )
50+ useEventListener ( window , 'mousedown' , onInteraction , listenerOptions )
4851 }
4952
5053 function scrollToTop ( ) {
@@ -58,17 +61,11 @@ export function useScrollToTop(options: UseScrollToTopOptions) {
5861 const start = window . scrollY
5962 if ( start <= 0 ) return
6063
64+ isScrolling . value = true
65+
6166 const startTime = performance . now ( )
6267 const change = - start
6368
64- const cleanup = [
65- useEventListener ( window , 'wheel' , cancel , { passive : true } ) ,
66- useEventListener ( window , 'touchstart' , cancel , { passive : true } ) ,
67- useEventListener ( window , 'mousedown' , cancel , { passive : true } ) ,
68- ]
69-
70- stopInteractionListeners = ( ) => cleanup . forEach ( stop => stop ( ) )
71-
7269 // Start the frame-by-frame scroll animation.
7370 function animate ( ) {
7471 const elapsed = performance . now ( ) - startTime
@@ -77,7 +74,7 @@ export function useScrollToTop(options: UseScrollToTopOptions) {
7774
7875 window . scrollTo ( { top : y } )
7976
80- if ( t < 1 ) {
77+ if ( t < 1 && isScrolling . value ) {
8178 rafId = requestAnimationFrame ( animate )
8279 } else {
8380 cancel ( )
@@ -87,7 +84,7 @@ export function useScrollToTop(options: UseScrollToTopOptions) {
8784 rafId = requestAnimationFrame ( animate )
8885 }
8986
90- onBeforeUnmount ( cancel )
87+ tryOnScopeDispose ( cancel )
9188
9289 return {
9390 scrollToTop,
0 commit comments