How the scroll-to.js Utility Implements Smooth Scrolling to Anchor Elements in Vue-Element-Admin
The scroll-to.js utility achieves smooth scrolling by combining a quadratic ease-in-out easing function with a requestAnimationFrame loop that incrementally updates the scroll position across all possible browser scroll containers.
The scroll-to.js module in the PanJiaChen/vue-element-admin repository provides a lightweight, cross-browser solution for animating scroll movements to anchor elements. Unlike native CSS scroll-behavior, this utility offers precise control over duration, easing curves, and completion callbacks, making it ideal for Vue.js applications that require programmatic navigation.
Core Implementation in scroll-to.js
The entire logic resides in src/utils/scroll-to.js, where the utility handles browser inconsistencies through a series of helper functions before executing the main animation loop.
Quadratic Easing with Math.easeInOutQuad
The utility extends the global Math object with a custom easing function that calculates the interpolated scroll position using a quadratic ease-in-out algorithm:
Math.easeInOutQuad = function(t, b, c, d) {
t /= d / 2
if (t < 1) {
return c / 2 * t * t + b
}
t--
return -c / 2 * (t * (t - 2) - 1) + b
}
This function accepts the current time (t), start value (b), total change (c), and duration (d), returning the eased position that creates the characteristic smooth acceleration and deceleration effect.
Cross-Browser Animation Frame Shim
To ensure compatibility with older browsers, the utility creates a requestAnimFrame shim that falls back to setTimeout when the native API is unavailable:
const requestAnimFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
})()
This maintains approximately 60 frames per second regardless of browser capabilities.
Unified Scroll Position Management
Different browsers expose the scroll position on different DOM elements. The utility solves this by writing to and reading from three possible locations simultaneously:
The move function updates all potential scroll containers:
function move(amount) {
document.documentElement.scrollTop = amount
document.body.parentNode.scrollTop = amount
document.body.scrollTop = amount
}
The position function retrieves the current scroll offset by checking the same elements:
function position() {
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
}
This approach ensures the animation works consistently across WebKit, Gecko, and legacy Internet Explorer environments.
The scrollTo Function and Animation Loop
The main scrollTo function orchestrates the animation by calculating initial values and executing a recursive loop:
export function scrollTo(to, duration, callback) {
const start = position()
const change = to - start
const increment = 20
let currentTime = 0
const animateScroll = function() {
currentTime += increment
const val = Math.easeInOutQuad(currentTime, start, change, duration)
move(val)
if (currentTime < duration) {
requestAnimFrame(animateScroll)
} else {
if (callback && typeof callback === 'function') {
callback()
}
}
}
animateScroll()
}
The function accepts three parameters: the target scroll position (to), the animation duration in milliseconds (duration), and an optional callback executed upon completion. The animation increments every 20 milliseconds, applying the eased value to all scroll containers until the duration elapses.
Practical Usage Examples
Scrolling to a Specific Element
To scroll to an anchor element manually, calculate its offset and invoke the utility:
import { scrollTo } from '@/utils/scroll-to'
function scrollToHeader() {
const header = document.getElementById('section-header')
if (header) {
// Scroll to element's top position over 800ms with completion callback
scrollTo(header.offsetTop, 800, () => {
console.log('Smooth scroll completed')
})
}
}
Integrating with Vue Router Hash Navigation
The utility commonly handles hash-based navigation in Vue Router guards:
import { scrollTo } from '@/utils/scroll-to'
router.afterEach((to, from) => {
if (to.hash) {
const target = document.querySelector(to.hash)
if (target) {
// Animate to hash target over 600ms
scrollTo(target.offsetTop, 600)
}
}
})
This pattern ensures that clicking anchor links triggers the smooth animation rather than instantaneous jumps.
Summary
- The
scroll-to.jsutility insrc/utils/scroll-to.jsprovides a framework-agnostic smooth scrolling solution for Vue-element-admin. - It implements quadratic ease-in-out easing through
Math.easeInOutQuadto create natural acceleration and deceleration curves. - A cross-browser shim ensures 60fps animation via
requestAnimFrame, falling back tosetTimeoutfor older browsers. - The utility writes to three scroll containers simultaneously (
documentElement,body.parentNode, andbody) to handle browser inconsistencies. - The main
scrollTo(to, duration, callback)function accepts a target position, duration in milliseconds, and optional completion callback.
Frequently Asked Questions
What easing function does scroll-to.js use?
The utility uses a quadratic ease-in-out algorithm defined as Math.easeInOutQuad. This function calculates intermediate scroll positions by interpolating between the start and target values using a parabolic curve, creating smooth acceleration at the beginning and deceleration at the end of the animation.
How does scroll-to.js handle cross-browser compatibility?
The module addresses browser differences through two primary mechanisms. First, it provides a requestAnimFrame shim that falls back to setTimeout when native animation frames are unavailable. Second, it reads from and writes to three potential scroll containers—document.documentElement, document.body.parentNode, and document.body—ensuring the animation works regardless of which element exposes the scroll position in a given browser.
Can I use scroll-to.js without Vue Router?
Yes, the utility is completely framework-agnostic. While commonly integrated with Vue Router navigation guards, you can import and invoke scrollTo from any JavaScript context. Simply pass the target pixel offset (often obtained via element.offsetTop), the desired duration in milliseconds, and an optional callback function to execute upon completion.
What is the default duration if not specified?
The scrollTo function requires an explicit duration parameter; there is no hardcoded default value within the utility itself. When calling the function, you must provide the duration in milliseconds (for example, 800 for 800ms). If you need a default behavior in your application, you should wrap the utility in a helper function that supplies a fallback value before invoking scrollTo.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →