-
Star
(336)
You must be signed in to star a gist -
Fork
(54)
You must be signed in to fork a gist
-
-
Save joshbeckman/6764939 to your computer and use it in GitHub Desktop.
document.getElementsByTagName('button')[0].onclick = function () { | |
scrollTo(document.body, 0, 1250); | |
} | |
function scrollTo(element, to, duration) { | |
var start = element.scrollTop, | |
change = to - start, | |
currentTime = 0, | |
increment = 20; | |
var animateScroll = function(){ | |
currentTime += increment; | |
var val = Math.easeInOutQuad(currentTime, start, change, duration); | |
element.scrollTop = val; | |
if(currentTime < duration) { | |
setTimeout(animateScroll, increment); | |
} | |
}; | |
animateScroll(); | |
} | |
//t = current time | |
//b = start value | |
//c = change in value | |
//d = duration | |
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; | |
}; |
how do work with scroll-snap-type: x mandatory;
const scrollTo = function(to, duration) { const element = document.scrollingElement || document.documentElement, start = element.scrollTop, change = to - start, startDate = +new Date(), // t = current time // b = start value // c = change in value // d = duration 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; }, animateScroll = function() { const currentDate = +new Date(); const currentTime = currentDate - startDate; element.scrollTop = parseInt(easeInOutQuad(currentTime, start, change, duration)); if(currentTime < duration) { requestAnimationFrame(animateScroll); } else { element.scrollTop = to; } }; animateScroll(); };
Here's the code a bit modernized. Now it's a lot smoother and works in Safari as well. If you don't work with babel then replace
const
withvar
.
Thanks. it is a lot more soomother
I also changed it to use performance.now()
:
var scrollTo = function(to, duration) {
var element = document.scrollingElement || document.documentElement,
start = element.scrollTop,
change = to - start,
startTs = performance.now(),
// t = current time
// b = start value
// c = change in value
// d = duration
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;
},
animateScroll = function(ts) {
var currentTime = ts - startTs;
element.scrollTop = parseInt(easeInOutQuad(currentTime, start, change, duration));
if(currentTime < duration) {
requestAnimationFrame(animateScroll);
}
else {
element.scrollTop = to;
}
};
requestAnimationFrame(animateScroll);
};
For the utility if you just want to paste this around, here is minimized version of the requestAnimationFrame
version:
var scrollTo=function(l,t){var c=document.scrollingElement||document.documentElement,m=c.scrollTop,a=l-m,s=performance.now(),i=function(o){var n,e,r=o-s;c.scrollTop=parseInt((n=r,e=m,o=a,(n/=t/2)<1?o/2*n*n+e:-o/2*(--n*(n-2)-1)+e)),r<t?requestAnimationFrame(i):c.scrollTop=l};requestAnimationFrame(i)};
Then use it like the others
scrollTo(150, 1000);
How would you go about making a linear function, no easing, just scroll down evenly in a set duration? Thank you!
@Alesvetina you would change the easing function for easeInOutQuad = function (t) { ...
to simply this:
function (t) { return t; }
Here's An updated version that satisfies much more strict ESLint parameters, plus new ES syntax:
export const animateScrollTo = (to, duration) => {
const element = document.scrollingElement || document.documentElement;
const start = element.scrollTop;
const change = to - start;
const startDate = +new Date();
// t = current time
// b = start value
// c = change in value
// d = duration
const easeInOutQuad = (t, b, c, d) => {
let t2 = t;
t2 /= d / 2;
if (t2 < 1) return (c / 2) * t2 * t2 + b;
t2 -= 1;
return (-c / 2) * (t2 * (t2 - 2) - 1) + b;
};
const animateScroll = () => {
const currentDate = +new Date();
const currentTime = currentDate - startDate;
element.scrollTop = parseInt(easeInOutQuad(currentTime, start, change, duration), 10);
if (currentTime < duration) {
requestAnimationFrame(animateScroll);
} else {
element.scrollTop = to;
}
};
animateScroll();
};
Hi, an alternative version with top and left props (typescript).
const scrollTo = ({element, top, left, duration}: {
element: HTMLElement,
top: number,
left: number,
duration: number,
}) => {
const startTop = element.scrollTop;
const startLeft = element.scrollLeft;
const changeTop = top - startTop;
const changeLeft = left - startLeft;
const startDate = new Date().getTime();
const animateScroll = function(){
const currentDate = new Date().getTime();
const currentTime = currentDate - startDate;
element.scrollTop = easeInOutQuad(currentTime, startTop, changeTop, duration);
element.scrollLeft = easeInOutQuad(currentTime, startLeft, changeLeft, duration);
if(currentTime < duration) {
requestAnimationFrame(animateScroll);
} else {
element.scrollTop = top;
element.scrollLeft = left;
}
};
animateScroll();
}
@KarloZKvasin I think your update is not properly typed.
easeInOutQuad
is set to return anEaseInOutQuadOptions
. Same goes forsmoothScroll
, set to return aSmoothScrollOptions
.You should dump these interfaces and type each function argument instead.
Updated: