Fix jarring smooth scroll with sine-based easing
All checks were successful
build-website / build (push) Successful in 1m31s
All checks were successful
build-website / build (push) Successful in 1m31s
- Replace easeInOutQuad with easeInOutSine for natural motion - Add user interruption handling (cancel on wheel/touch/keyboard) - Add accessibility support for prefers-reduced-motion - Optimize duration from 1800ms to 1200ms - Add proper cleanup of event listeners and animation frames - Update all component references to use new duration The sine-based easing creates smooth, consistent motion throughout the entire scroll duration without jarring speed changes.
This commit is contained in:
@@ -65,11 +65,11 @@ const handleSmoothScroll = (event: Event, href: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
scrollToElement(href, 1800) // 1.8 seconds for smoother scrolling
|
scrollToElement(href, 1200) // Smooth scrolling with sine easing
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollToTop = () => {
|
const scrollToTop = () => {
|
||||||
smoothScrollToTop(1800) // 1.8 seconds for smoother scrolling
|
smoothScrollToTop(1200) // Smooth scrolling with sine easing
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -101,11 +101,11 @@ const handlePhoneClick = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleServicesClick = () => {
|
const handleServicesClick = () => {
|
||||||
scrollToElement('#services', 1800) // 1.8 seconds for smoother scrolling
|
scrollToElement('#services', 1200) // Smooth scrolling with sine easing
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleScrollToExplore = () => {
|
const handleScrollToExplore = () => {
|
||||||
scrollToElement('#services', 1800) // Scroll to services section
|
scrollToElement('#services', 1200) // Smooth scroll to services section
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleVideoVisibility = () => {
|
const handleVideoVisibility = () => {
|
||||||
@@ -136,7 +136,7 @@ const initSmoothScroll = () => {
|
|||||||
if (!href) {
|
if (!href) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
scrollToElement(href, 1800) // 1.8 seconds for smoother scrolling
|
scrollToElement(href, 1200) // Smooth scrolling with sine easing
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,40 @@
|
|||||||
export const useSmoothScroll = () => {
|
export const useSmoothScroll = () => {
|
||||||
const smoothScrollTo = (target: number | Element, duration: number = 1800) => {
|
let animationFrameId: number | null = null
|
||||||
|
|
||||||
|
// Cancel any ongoing scroll animation
|
||||||
|
const cancelScroll = () => {
|
||||||
|
if (animationFrameId) {
|
||||||
|
cancelAnimationFrame(animationFrameId)
|
||||||
|
animationFrameId = null
|
||||||
|
}
|
||||||
|
// Clean up event listeners
|
||||||
|
window.removeEventListener('wheel', cancelScroll)
|
||||||
|
window.removeEventListener('touchstart', cancelScroll)
|
||||||
|
window.removeEventListener('keydown', cancelScroll)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smooth sine-based easing function for natural motion
|
||||||
|
const easeInOutSine = (t: number): number => {
|
||||||
|
return -(Math.cos(Math.PI * t) - 1) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
const smoothScrollTo = (target: number | Element, duration: number = 1200) => {
|
||||||
|
// Check for reduced motion preference
|
||||||
|
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||||
|
|
||||||
const targetPosition = typeof target === 'number'
|
const targetPosition = typeof target === 'number'
|
||||||
? target
|
? target
|
||||||
: target.getBoundingClientRect().top + window.pageYOffset
|
: target.getBoundingClientRect().top + window.pageYOffset
|
||||||
|
|
||||||
|
if (prefersReducedMotion) {
|
||||||
|
// Instant scroll for accessibility
|
||||||
|
window.scrollTo(0, targetPosition)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel any previously running scroll animation
|
||||||
|
cancelScroll()
|
||||||
|
|
||||||
const startPosition = window.pageYOffset
|
const startPosition = window.pageYOffset
|
||||||
const distance = targetPosition - startPosition
|
const distance = targetPosition - startPosition
|
||||||
let startTime: number | null = null
|
let startTime: number | null = null
|
||||||
@@ -11,32 +43,33 @@ export const useSmoothScroll = () => {
|
|||||||
if (startTime === null) startTime = currentTime
|
if (startTime === null) startTime = currentTime
|
||||||
const timeElapsed = currentTime - startTime
|
const timeElapsed = currentTime - startTime
|
||||||
const progress = Math.min(timeElapsed / duration, 1)
|
const progress = Math.min(timeElapsed / duration, 1)
|
||||||
|
const easedProgress = easeInOutSine(progress)
|
||||||
|
|
||||||
// Easing function for smoother, more consistent motion
|
window.scrollTo(0, startPosition + distance * easedProgress)
|
||||||
const easeInOutQuad = (t: number) => {
|
|
||||||
return t < 0.5
|
|
||||||
? 2 * t * t
|
|
||||||
: 1 - Math.pow(-2 * t + 2, 2) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
window.scrollTo(0, startPosition + distance * easeInOutQuad(progress))
|
|
||||||
|
|
||||||
if (progress < 1) {
|
if (progress < 1) {
|
||||||
requestAnimationFrame(animation)
|
animationFrameId = requestAnimationFrame(animation)
|
||||||
|
} else {
|
||||||
|
cancelScroll() // Clean up when animation completes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(animation)
|
// Add listeners to detect user interruption
|
||||||
|
window.addEventListener('wheel', cancelScroll, { passive: true })
|
||||||
|
window.addEventListener('touchstart', cancelScroll, { passive: true })
|
||||||
|
window.addEventListener('keydown', cancelScroll, { once: true })
|
||||||
|
|
||||||
|
animationFrameId = requestAnimationFrame(animation)
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollToElement = (selector: string, duration: number = 1800) => {
|
const scrollToElement = (selector: string, duration: number = 1200) => {
|
||||||
const element = document.querySelector(selector)
|
const element = document.querySelector(selector)
|
||||||
if (element) {
|
if (element) {
|
||||||
smoothScrollTo(element, duration)
|
smoothScrollTo(element, duration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollToTop = (duration: number = 1800) => {
|
const scrollToTop = (duration: number = 1200) => {
|
||||||
smoothScrollTo(0, duration)
|
smoothScrollTo(0, duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user