blob: e33586f5c00eedef69b9f11a30ddc760fc5a410a [file] [log] [blame]
Matteo Scandolo46b56102015-12-16 14:23:08 -08001/*
2 * https://github.com/alicelieutier/smoothScroll/
3 * A teeny tiny, standard compliant, smooth scroll script with ease-in-out effect and no jQuery (or any other dependancy, FWIW).
4 * MIT License
5 */
6window.smoothScroll = (function(){
7// We do not want this script to be applied in browsers that do not support those
8// That means no smoothscroll on IE9 and below.
9if(document.querySelectorAll === void 0 || window.pageYOffset === void 0 || history.pushState === void 0) { return; }
10
11// Get the top position of an element in the document
12var getTop = function(element) {
13 // return value of html.getBoundingClientRect().top ... IE : 0, other browsers : -pageYOffset
14 if(element.nodeName === 'HTML') return -window.pageYOffset
15 return element.getBoundingClientRect().top + window.pageYOffset;
16}
17// ease in out function thanks to:
18// http://blog.greweb.fr/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/
19var easeInOutCubic = function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
20
21// calculate the scroll position we should be in
22// given the start and end point of the scroll
23// the time elapsed from the beginning of the scroll
24// and the total duration of the scroll (default 500ms)
25var position = function(start, end, elapsed, duration) {
26 if (elapsed > duration) return end;
27 return start + (end - start) * easeInOutCubic(elapsed / duration); // <-- you can change the easing funtion there
28 // return start + (end - start) * (elapsed / duration); // <-- this would give a linear scroll
29}
30
31// we use requestAnimationFrame to be called by the browser before every repaint
32// if the first argument is an element then scroll to the top of this element
33// if the first argument is numeric then scroll to this location
34// if the callback exist, it is called when the scrolling is finished
35var smoothScroll = function(el, duration, callback){
36 duration = duration || 500;
37 var start = window.pageYOffset;
38
39 if (typeof el === 'number') {
40 var end = parseInt(el);
41 } else {
42 var end = getTop(el);
43 }
44
45 var clock = Date.now();
46 var requestAnimationFrame = window.requestAnimationFrame ||
47 window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
48 function(fn){window.setTimeout(fn, 15);};
49
50 var step = function(){
51 var elapsed = Date.now() - clock;
52 window.scroll(0, position(start, end, elapsed, duration));
53 if (elapsed > duration) {
54 if (typeof callback === 'function') {
55 callback(el);
56 }
57 } else {
58 requestAnimationFrame(step);
59 }
60 }
61 step();
62}
63
64var linkHandler = function(ev) {
65 ev.preventDefault();
66
67 if (location.hash !== this.hash) {
68 //NOTE(@ajoslin): Changed this line to stop $digest errors
69 //window.history.pushState(null, null, this.hash)
70 angular.element(document).injector().get('$location').hash(this.hash);
71 }
72 // using the history api to solve issue #1 - back doesn't work
73 // most browser don't update :target when the history api is used:
74 // THIS IS A BUG FROM THE BROWSERS.
75 // change the scrolling duration in this call
76 var targetEl = document.getElementById(this.hash.substring(1));
77 if (targetEl) {
78 smoothScroll(document.getElementById(this.hash.substring(1)), 500, function(el) {
79 location.replace('#' + el.id)
80 // this will cause the :target to be activated.
81 });
82 }
83}
84
85// We look for all the internal links in the documents and attach the smoothscroll function
86document.addEventListener("DOMContentLoaded", function () {
87 var internal = document.querySelectorAll('a[href^="#"]'), a;
88 for(var i=internal.length; a=internal[--i];){
89 a.addEventListener("click", linkHandler, false);
90 }
91});
92
93// return smoothscroll API
94return smoothScroll;
95
96})();
97