Use smooth scroll function to navigate to anchor

Using a eased RAF function to scroll to a specific target
Avoid using `scrollIntoView` or smooth behavior as it doesn't work on Safari and others.
This commit is contained in:
2022-07-11 16:42:28 +02:00
parent f81a468a04
commit ae4ea7f4fa
6 changed files with 73 additions and 7 deletions

View File

@@ -0,0 +1,9 @@
/**
* Ease: In Out Quart
*/
export const easeInOutQuart = (t: number, b: number, c: number, d: number) => {
t /= d/2
if (t < 1) return c/2 * t * t * t * t + b
t -= 2
return -c / 2 * (t * t * t * t - 2) + b
}

View File

@@ -1,3 +1,6 @@
import { easeInOutQuart } from './easing'
/**
* Throttle function
*/
@@ -149,4 +152,51 @@ export const scrollToTop = (delay?: number) => {
} else {
scroll()
}
}
/**
* Smooth Scroll to an element
* @description Promised based
* @url https://www.youtube.com/watch?v=oUSvlrDTLi4
*/
const smoothScrollPromise = (target: HTMLElement, duration: number = 1600): Promise<void> => {
const position = target.getBoundingClientRect().top + 1
const startPosition = window.scrollY
const distance = position - startPosition
let startTime: number = null
// Return Promise
return new Promise((resolve) => {
if (!(target instanceof Element)) throw new TypeError('Argument 1 must be an Element')
if (typeof window === 'undefined') return
// Scroll to animation
const animation = (currentTime: number) => {
if (startTime === null) startTime = currentTime
const timeElapsed = currentTime - startTime
// Create easing value
const easedYPosition = easeInOutQuart(timeElapsed, startPosition, distance, duration)
// Scroll to Y position
window.scrollTo(0, easedYPosition)
// Loop or end animation
if (timeElapsed < duration) {
requestAnimationFrame(animation)
} else {
return resolve()
}
}
requestAnimationFrame(animation)
})
}
export const smoothScroll = async (hash: string, changeHash: boolean = true, callback?: Function) => {
const target = document.getElementById(hash)
smoothScrollPromise(target).then(() => {
if (changeHash) {
location.hash = hash
}
callback && callback()
})
}