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.js utility in src/utils/scroll-to.js provides a framework-agnostic smooth scrolling solution for Vue-element-admin.
  • It implements quadratic ease-in-out easing through Math.easeInOutQuad to create natural acceleration and deceleration curves.
  • A cross-browser shim ensures 60fps animation via requestAnimFrame, falling back to setTimeout for older browsers.
  • The utility writes to three scroll containers simultaneously (documentElement, body.parentNode, and body) 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →