Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@SamJakob
Created October 22, 2018 17:10
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save SamJakob/c9175a4c2440e1b14b0b8cf7d99d2d24 to your computer and use it in GitHub Desktop.
Save SamJakob/c9175a4c2440e1b14b0b8cf7d99d2d24 to your computer and use it in GitHub Desktop.
A vue-js supported smooth-scroll module based on a Stack Overflow answer by Manuel Otto.
const SmoothScroll = (target, speed, smooth) => {
if (target === document)
target = (document.documentElement || document.body.parentNode || document.body) // cross browser support for document scrolling
let moving = false;
let pos = target.scrollTop;
target.addEventListener('mousewheel', scrolled, false)
/* target.addEventListener('DOMMouseScroll', scrolled, false) */
function scrolled(e) {
e.preventDefault(); // disable default scrolling
let delta = e.delta || e.wheelDelta;
if (delta === undefined) {
//we are on firefox
delta = -e.detail;
}
delta = Math.max(-1, Math.min(1, delta)) // cap the delta to [-1,1] for cross browser consistency
pos += -delta * speed
pos = Math.max(0, Math.min(pos, (target.scrollHeight - target.clientHeight) + (smooth * 2))) // limit scrolling
if (!moving) update()
}
function update() {
moving = true
let delta = (pos - target.scrollTop) / smooth;
delta -= 1;
if(pos - target.scrollTop === smooth * 2)
delta = 0;
target.scrollTop += delta
if (Math.abs(delta) > 0.5)
requestFrame(update)
else
moving = false
}
var requestFrame = function() { // requestAnimationFrame cross browser
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(func) {
window.setTimeout(func, 1000 / 50);
}
);
}()
}
export default SmoothScroll;
<template>
...
</template>
<script>
import SmoothScroll from 'assets/js/smoothscroll';
export default {
mounted () {
if(!window.smoothScroll){
window.smoothScroll = SmoothScroll(document, 120, 12);
}
}
}
</script>
@kujohn
Copy link

kujohn commented Apr 15, 2019

For Safari compatibility, we need to force using document.body to work. I have this added before passing to use this script:

const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
const doc = isSafari ? document.body : document
SmoothScroll(doc, 120, 12)

Or this script can be modified to be more specific.

@Ayanrocks
Copy link

What to do if you drag the scrollbar and it starts to scroll from there? Now its jumping to the last scrolled position.

@kcharlesbusiness
Copy link

Works well but I think this only works with Google Chrome

@joeljose24
Copy link

Hey Sam, I really love how the script works but I have a very niche issue where the script sort of breaks and was wondering if you could give me some insight on how to solve it.
Basically, I'm in a position where I need to call the function multiple times to change the speed of the scroll at different points of the webpage. This causes the smoothness aspect of the script to stop working. Any thoughts?

@SamJakob
Copy link
Author

@joeljose24 Thank you :)
Have you tried moving the speed variable outside of the function or moving it to the scope where you change the speed?

@joeljose24
Copy link

@SamJakob not exactly sure what you mean by moving the variable outside the function or to a different scope.. My exact scenario is I have a main webpage with a bunch of components. As the main webpage loads, if smoothscroll.js isn't in use, it calls the function. Beyond that, there is a component at a point on the webpage within which I call the smoothscroll.js function to slow it down.

@SamJakob
Copy link
Author

@joeljose24
So if you see on line 1, the function is defined as const SmoothScroll = (target, speed, smooth) => {

I'd suggest taking speed out of the function arguments, so it's const SmoothScroll = (target, smooth) => { and just defining speed as a variable outside of the function. e.g.,

// Set the initial speed.
let speed = 120;

export function setSpeed(newScrollSpeed) {
    speed = newScrollSpeed;
}

const SmoothScroll = (target, smooth) => {
// .. rest of the function from above (by not specifying speed as an argument it'll use the one defined above which
// will let us call setSpeed to change it)
}

export default SmoothScroll;

Then in the Vue example, you might call it like so:

import SmoothScroll, { setScrollSpeed } from 'assets/js/smoothscroll';

// ...

mounted () {
  if(!window.smoothScroll){
    window.smoothScroll = SmoothScroll(document, 12);
  }
}

// ...

Then, when you want to change the scroll speed like so:

setScrollSpeed(240);

@joeljose24
Copy link

@SamJakob hey sam, I figured a workaround for this, I'm just disabling window scroll for specific points and enabling it after a time out.
Regardless, this solution still helped me solve a bug that I had because of the scroll disabling so, thank you so much for taking the time to answer my doubt! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment