-
-
Save dezinezync/5487119 to your computer and use it in GitHub Desktop.
function scrollTo(Y, duration, easingFunction, callback) { | |
var start = Date.now(), | |
elem = document.documentElement.scrollTop?document.documentElement:document.body, | |
from = elem.scrollTop; | |
if(from === Y) { | |
callback(); | |
return; /* Prevent scrolling to the Y point if already there */ | |
} | |
function min(a,b) { | |
return a<b?a:b; | |
} | |
function scroll(timestamp) { | |
var currentTime = Date.now(), | |
time = min(1, ((currentTime - start) / duration)), | |
easedT = easingFunction(time); | |
elem.scrollTop = (easedT * (Y - from)) + from; | |
if(time < 1) requestAnimationFrame(scroll); | |
else | |
if(callback) callback(); | |
} | |
requestAnimationFrame(scroll) | |
} | |
/* bits and bytes of the scrollTo function inspired by the works of Benjamin DeCock */ | |
/* | |
* Easing Functions - inspired from http://gizma.com/easing/ | |
* only considering the t value for the range [0, 1] => [0, 1] | |
*/ | |
var easing = { | |
// no easing, no acceleration | |
linear: function (t) { return t }, | |
// accelerating from zero velocity | |
easeInQuad: function (t) { return t*t }, | |
// decelerating to zero velocity | |
easeOutQuad: function (t) { return t*(2-t) }, | |
// acceleration until halfway, then deceleration | |
easeInOutQuad: function (t) { return t<.5 ? 2*t*t : -1+(4-2*t)*t }, | |
// accelerating from zero velocity | |
easeInCubic: function (t) { return t*t*t }, | |
// decelerating to zero velocity | |
easeOutCubic: function (t) { return (--t)*t*t+1 }, | |
// acceleration until halfway, then deceleration | |
easeInOutCubic: function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }, | |
// accelerating from zero velocity | |
easeInQuart: function (t) { return t*t*t*t }, | |
// decelerating to zero velocity | |
easeOutQuart: function (t) { return 1-(--t)*t*t*t }, | |
// acceleration until halfway, then deceleration | |
easeInOutQuart: function (t) { return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t }, | |
// accelerating from zero velocity | |
easeInQuint: function (t) { return t*t*t*t*t }, | |
// decelerating to zero velocity | |
easeOutQuint: function (t) { return 1+(--t)*t*t*t*t }, | |
// acceleration until halfway, then deceleration | |
easeInOutQuint: function (t) { return t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t } | |
} | |
/* Add the following to you main js file | |
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ | |
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating | |
// requestAnimationFrame polyfill by Erik Möller | |
// fixes from Paul Irish and Tino Zijdel | |
(function() { | |
var lastTime = 0; | |
var vendors = ['ms', 'moz', 'webkit', 'o']; | |
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { | |
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; | |
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] | |
|| window[vendors[x]+'CancelRequestAnimationFrame']; | |
} | |
if (!window.requestAnimationFrame) | |
window.requestAnimationFrame = function(callback, element) { | |
var currTime = new Date().getTime(); | |
var timeToCall = Math.max(0, 16 - (currTime - lastTime)); | |
var id = window.setTimeout(function() { callback(currTime + timeToCall); }, | |
timeToCall); | |
lastTime = currTime + timeToCall; | |
return id; | |
}; | |
if (!window.cancelAnimationFrame) | |
window.cancelAnimationFrame = function(id) { | |
clearTimeout(id); | |
}; | |
}()); */ |
I've forked Nikhil's scroll.easing.js to https://gist.github.com/Eyal-Shalev/24fa303cc98ee1ea4b4a.
Usage example:
var SomeHTMLContainer = document.getElementById('some-scroll-container');
var scrollTime = 1000;
var someCallbackFunction = function() {console.log('hello');};
var scroller = new Scroller(SomeHTMLContainer, scrollTime)
.easing(Scroller.EASING.easeInQuad)
.callback(someCallbackFunction);
var top = 50, left = 150;
scroller.to(top,left);
Here's my example and keeping it Vanilla still (no jQuery). Also to set the scene I placed the event listener on the ul
that contains li
s with my anchors inside containing the hash values.
<h1 id="table-of-contents">Table of Contents</h1>
<ul>
<li><a href="#about-the-author">About The Author</a></li>
<li><a href="#contributing">Contributing</a></li>
<li><a href="#about-sass">About Sass</a>
<ul>
<li><a href="#ruby-sass-or-libsass">Ruby Sass Or LibSass</a></li>
<li><a href="#sass-or-scss">Sass Or SCSS</a></li>
<li><a href="#other-preprocessors">Other Preprocessors</a></li>
</ul>
</li>
<li><a href="#introduction">Introduction</a>
<ul>
<li><a href="#why-a-styleguide">Why A Styleguide</a></li>
<li><a href="#disclaimer">Disclaimer</a></li>
<li><a href="#key-principles">Key Principles</a></li>
</ul>
</li>
</ul>
var sgl_toc_list = document.getElementById('table-of-contents').nextElementSibling; // grab the ul wth the links inside it
function sglScrollmation(event) {
var scroll_speed = 800,
hash_value = event.target.attributes.href.value.substring(1), // remove the # from the returned value
hash_section = document.getElementById(hash_value).offsetTop,
ease_motion = easing.easeInOutCubic;
scrollTo(hash_section, scroll_speed, ease_motion);
}
sgl_toc_list.addEventListener('click', sglScrollmation);
@dezinezync would be kind enough to add some information around under which license is this code being made public? Specifically the easing functions as they are quite different than those found at http://gizma.com/easing/.
The reason I am asking because without a specified license its difficult to use this code in some projects. Also would like to give attribution to the author properly.
First: this script is really good, works nice with little payload, well done!
But: When using addEventListener, like grayghostvisuals does, the browser performs the "normal" anchor-jump for a split second and then the nice scrolling takes place, via the script as it is supposed to be.
Any ideas how to prevent the first browser-scroll? I tried "onclick" on the container, instead of addEventListener, didn't work at all.
p.s.
firebug reported an "easedT = easingFunction(time); is not a function"-error on loading a page, so i added:
if (easingFunction)
{
var currentTime = Date.now(),
time = min(1, ((currentTime - start) / duration)),
easedT = easingFunction(time);
elem.scrollTop = (easedT * (Y - from)) + from;
if(time < 1) requestAnimationFrame(scroll);
else if(callback) callback();
}
to function "scroll" to prevent this, which does not affect my primary issue.
There seems to be a bug to where the document will not scroll if scrollTop is 0 in some browsers. And some browsers will always report 0 for scrollTop. Therefore, I've found a patch for line 4.
elem = (document.documentElement.scrollTop++ !== 0) ? document.documentElement : document.body,
This attempts to increment the position to see if changing it does anything. If it does, then use it, else use document.body.
Whew…
Turns out that wasn't a good fix either. Firefox didn't like the incrementer.
Instead you should set the scrollTop to itself plus one, then check it.
Like so:
// Used to see which object we should scroll on
document.documentElement.scrollTop = document.documentElement.scrollTop + 1;
var start = Date.now(),
elem = (document.documentElement.scrollTop !== 0) ? document.documentElement : document.body,
from = elem.scrollTop,
...
Hope that helps.
This gist was very helpful for me. Because we ended up a bit confused about element detection I put all the things together.
function scrollIt(destination, duration = 200, easing = 'linear', callback) {
// define timing functions
let easings = {
// no easing, no acceleration
linear(t) {
return t;
},
// accelerating from zero velocity
easeInQuad(t) {
return t * t;
},
// decelerating to zero velocity
easeOutQuad(t) {
return t * (2 - t);
},
// acceleration until halfway, then deceleration
easeInOutQuad(t) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
},
// accelerating from zero velocity
easeInCubic(t) {
return t * t * t;
},
// decelerating to zero velocity
easeOutCubic(t) {
return (--t) * t * t + 1;
},
// acceleration until halfway, then deceleration
easeInOutCubic(t) {
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
},
// accelerating from zero velocity
easeInQuart(t) {
return t * t * t * t;
},
// decelerating to zero velocity
easeOutQuart(t) {
return 1 - (--t) * t * t * t;
},
// acceleration until halfway, then deceleration
easeInOutQuart(t) {
return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
},
// accelerating from zero velocity
easeInQuint(t) {
return t * t * t * t * t;
},
// decelerating to zero velocity
easeOutQuint(t) {
return 1 + (--t) * t * t * t * t;
},
// acceleration until halfway, then deceleration
easeInOutQuint(t) {
return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t;
}
};
function checkElement() {
// returns document.documentElement for chrome and safari
// document.body for rest of the world
document.documentElement.scrollTop += 1;
let elm = (document.documentElement.scrollTop !== 0) ? document.documentElement : document.body;
document.documentElement.scrollTop -= 1;
return elm;
}
let element = checkElement();
let start = element.scrollTop;
let startTime = Date.now();
function scroll() {
let now = Date.now();
let time = Math.min(1, ((now - startTime) / duration));
let timeFunction = easings[easing](time);
element.scrollTop = (timeFunction * (destination - start)) + start;
if (element.scrollTop === destination) {
callback;
return;
}
requestAnimationFrame(scroll);
}
scroll();
}
And this is how to use it:
scrollIt(1000, 300, 'easeInQuad', console.log('done'));
or simple version with default arguments if you don't care about duration, easing and callback...
scrollIt(1000);
Hi, with Firefox (35, Developer Edition), I can't manage to scroll if the current scroll position is on top of the page. Any idea ?