github.com/GregorioDiStefano/go-file-storage@v0.0.0-20161001105139-5707ab351525/static/js/angular-smooth-scroll.js (about) 1 /*! 2 * Angular Smooth Scroll (ngSmoothScroll) 3 * Animates scrolling to elements, by David Oliveros. 4 * 5 * Callback hooks contributed by Ben Armston https://github.com/benarmston 6 * Easing support contributed by Willem Liu. https://github.com/willemliu 7 * Easing functions forked from Gaƫtan Renaudeau. https://gist.github.com/gre/1650294 8 * Infinite loop bugs in iOS and Chrome (when zoomed) by Alex Guzman. https://github.com/alexguzman 9 * Support for scrolling in custom containers by Joseph Matthias Goh. https://github.com/zephinzer 10 * Influenced by Chris Ferdinandi 11 * https://github.com/cferdinandi 12 * 13 * Version: 2.0.0 14 * License: MIT 15 */ 16 17 (function () { 18 'use strict'; 19 20 var module = angular.module('smoothScroll', []); 21 22 23 /** 24 * Smooth scrolls the window/div to the provided element. 25 * 26 * 20150713 EDIT - zephinzer 27 * Added new option - containerId to account for scrolling within a DIV 28 */ 29 var smoothScroll = function (element, options) { 30 options = options || {}; 31 32 // Options 33 var duration = options.duration || 800, 34 offset = options.offset || 0, 35 easing = options.easing || 'easeInOutQuart', 36 callbackBefore = options.callbackBefore || function() {}, 37 callbackAfter = options.callbackAfter || function() {}, 38 container = document.getElementById(options.containerId) || null, 39 containerPresent = (container != undefined && container != null); 40 41 /** 42 * Retrieve current location 43 */ 44 var getScrollLocation = function() { 45 if(containerPresent) { 46 return container.scrollTop; 47 } else { 48 if(window.pageYOffset) { 49 return window.pageYOffset; 50 } else { 51 return document.documentElement.scrollTop; 52 } 53 } 54 }; 55 56 /** 57 * Calculate easing pattern. 58 * 59 * 20150713 edit - zephinzer 60 * - changed if-else to switch 61 * @see http://archive.oreilly.com/pub/a/server-administration/excerpts/even-faster-websites/writing-efficient-javascript.html 62 */ 63 var getEasingPattern = function(type, time) { 64 switch(type) { 65 case 'easeInQuad': return time * time; // accelerating from zero velocity 66 case 'easeOutQuad': return time * (2 - time); // decelerating to zero velocity 67 case 'easeInOutQuad': return time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration 68 case 'easeInCubic': return time * time * time; // accelerating from zero velocity 69 case 'easeOutCubic': return (--time) * time * time + 1; // decelerating to zero velocity 70 case 'easeInOutCubic': return time < 0.5 ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration 71 case 'easeInQuart': return time * time * time * time; // accelerating from zero velocity 72 case 'easeOutQuart': return 1 - (--time) * time * time * time; // decelerating to zero velocity 73 case 'easeInOutQuart': return time < 0.5 ? 8 * time * time * time * time : 1 - 8 * (--time) * time * time * time; // acceleration until halfway, then deceleration 74 case 'easeInQuint': return time * time * time * time * time; // accelerating from zero velocity 75 case 'easeOutQuint': return 1 + (--time) * time * time * time * time; // decelerating to zero velocity 76 case 'easeInOutQuint': return time < 0.5 ? 16 * time * time * time * time * time : 1 + 16 * (--time) * time * time * time * time; // acceleration until halfway, then deceleration 77 default: return time; 78 } 79 }; 80 81 /** 82 * Calculate how far to scroll 83 */ 84 var getEndLocation = function(element) { 85 var location = 0; 86 if (element.offsetParent) { 87 do { 88 location += element.offsetTop; 89 element = element.offsetParent; 90 } while (element); 91 } 92 location = Math.max(location - offset, 0); 93 return location; 94 }; 95 96 // Initialize the whole thing 97 setTimeout( function() { 98 var currentLocation = null, 99 startLocation = getScrollLocation(), 100 endLocation = getEndLocation(element), 101 timeLapsed = 0, 102 distance = endLocation - startLocation, 103 percentage, 104 position, 105 scrollHeight, 106 internalHeight; 107 108 /** 109 * Stop the scrolling animation when the anchor is reached (or at the top/bottom of the page) 110 */ 111 var stopAnimation = function () { 112 currentLocation = getScrollLocation(); 113 if(containerPresent) { 114 scrollHeight = container.scrollHeight; 115 internalHeight = container.clientHeight + currentLocation; 116 } else { 117 scrollHeight = document.body.scrollheight; 118 internalHeight = window.innerHeight + currentLocation; 119 } 120 121 if ( 122 ( // condition 1 123 position == endLocation 124 ) || 125 ( // condition 2 126 currentLocation == endLocation 127 ) || 128 ( // condition 3 129 internalHeight >= scrollHeight 130 ) 131 ) { // stop 132 clearInterval(runAnimation); 133 callbackAfter(element); 134 } 135 }; 136 137 /** 138 * Scroll the page by an increment, and check if it's time to stop 139 */ 140 var animateScroll = function () { 141 timeLapsed += 16; 142 percentage = ( timeLapsed / duration ); 143 percentage = ( percentage > 1 ) ? 1 : percentage; 144 position = startLocation + ( distance * getEasingPattern(easing, percentage) ); 145 if(containerPresent) { 146 container.scrollTop = position; 147 } else { 148 window.scrollTo( 0, position ); 149 } 150 stopAnimation(); 151 }; 152 153 callbackBefore(element); 154 var runAnimation = setInterval(animateScroll, 16); 155 }, 0); 156 }; 157 158 159 // Expose the library in a factory 160 // 161 module.factory('smoothScroll', function() { 162 return smoothScroll; 163 }); 164 165 166 /** 167 * Scrolls the window to this element, optionally validating an expression 168 * 169 * 20150713 EDIT - zephinzer 170 * Added containerId to attributes for smooth scrolling within a DIV 171 */ 172 module.directive('smoothScroll', ['smoothScroll', function(smoothScroll) { 173 return { 174 restrict: 'A', 175 scope: { 176 callbackBefore: '&', 177 callbackAfter: '&', 178 }, 179 link: function($scope, $elem, $attrs) { 180 if ( typeof $attrs.scrollIf === 'undefined' || $attrs.scrollIf === 'true' ) { 181 setTimeout( function() { 182 183 var callbackBefore = function(element) { 184 if ( $attrs.callbackBefore ) { 185 var exprHandler = $scope.callbackBefore({ element: element }); 186 if (typeof exprHandler === 'function') { 187 exprHandler(element); 188 } 189 } 190 }; 191 192 var callbackAfter = function(element) { 193 if ( $attrs.callbackAfter ) { 194 var exprHandler = $scope.callbackAfter({ element: element }); 195 if (typeof exprHandler === 'function') { 196 exprHandler(element); 197 } 198 } 199 }; 200 201 smoothScroll($elem[0], { 202 duration: $attrs.duration, 203 offset: $attrs.offset, 204 easing: $attrs.easing, 205 callbackBefore: callbackBefore, 206 callbackAfter: callbackAfter, 207 containerId: $attrs.containerId 208 }); 209 }, 0); 210 } 211 } 212 }; 213 }]); 214 215 216 /** 217 * Scrolls to a specified element ID when this element is clicked 218 * 219 * 20150713 EDIT - zephinzer 220 * Added containerId to attributes for smooth scrolling within a DIV 221 */ 222 module.directive('scrollTo', ['smoothScroll', function(smoothScroll) { 223 return { 224 restrict: 'A', 225 scope: { 226 callbackBefore: '&', 227 callbackAfter: '&', 228 }, 229 link: function($scope, $elem, $attrs) { 230 var targetElement; 231 232 $elem.on('click', function(e) { 233 e.preventDefault(); 234 235 targetElement = document.getElementById($attrs.scrollTo); 236 if ( !targetElement ) return; 237 238 var callbackBefore = function(element) { 239 if ( $attrs.callbackBefore ) { 240 var exprHandler = $scope.callbackBefore({element: element}); 241 if (typeof exprHandler === 'function') { 242 exprHandler(element); 243 } 244 } 245 }; 246 247 var callbackAfter = function(element) { 248 if ( $attrs.callbackAfter ) { 249 var exprHandler = $scope.callbackAfter({element: element}); 250 if (typeof exprHandler === 'function') { 251 exprHandler(element); 252 } 253 } 254 }; 255 256 smoothScroll(targetElement, { 257 duration: $attrs.duration, 258 offset: $attrs.offset, 259 easing: $attrs.easing, 260 callbackBefore: callbackBefore, 261 callbackAfter: callbackAfter, 262 containerId: $attrs.containerId 263 }); 264 265 return false; 266 }); 267 } 268 }; 269 }]); 270 271 }());