This repository has been archived by the owner. It is now read-only.
Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
clas/js/smooth-scroll.js
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
167 lines (123 sloc)
5.69 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ============================================================= | |
Smooth Scroll v3.3 | |
Animate scrolling to anchor links, by Chris Ferdinandi. | |
http://gomakethings.com | |
Easing support contributed by Willem Liu. | |
https://github.com/willemliu | |
Easing functions forked from Gaëtan Renaudeau. | |
https://gist.github.com/gre/1650294 | |
URL history support contributed by Robert Pate. | |
https://github.com/robertpateii | |
Fixed header support contributed by Arndt von Lucadou. | |
https://github.com/a-v-l | |
Infinite loop bugs in iOS and Chrome (when zoomed) by Alex Guzman. | |
https://github.com/alexguzman | |
Free to use under the MIT License. | |
http://gomakethings.com/mit/ | |
* ============================================================= */ | |
window.smoothScroll = (function (window, document, undefined) { | |
'use strict'; | |
// Feature Test | |
if ( 'querySelector' in document && 'addEventListener' in window && Array.prototype.forEach ) { | |
// SELECTORS | |
var scrollToggles = document.querySelectorAll('[data-scroll]'); | |
// METHODS | |
// Run the smooth scroll animation | |
var runSmoothScroll = function (anchor, duration, easing, url) { | |
// SELECTORS | |
var startLocation = window.pageYOffset; | |
// Get the height of a fixed header if one exists | |
var scrollHeader = document.querySelector('[data-scroll-header]'); | |
var headerHeight = scrollHeader === null ? 0 : (scrollHeader.offsetHeight + scrollHeader.offsetTop); | |
// Set the animation variables to 0/undefined. | |
var timeLapsed = 0; | |
var percentage, position; | |
// METHODS | |
// Calculate the easing pattern | |
var easingPattern = function (type, time) { | |
if ( type == 'easeInQuad' ) return time * time; // accelerating from zero velocity | |
if ( type == 'easeOutQuad' ) return time * (2 - time); // decelerating to zero velocity | |
if ( type == 'easeInOutQuad' ) return time < 0.5 ? 2 * time * time : -1 + (4 - 2 * time) * time; // acceleration until halfway, then deceleration | |
if ( type == 'easeInCubic' ) return time * time * time; // accelerating from zero velocity | |
if ( type == 'easeOutCubic' ) return (--time) * time * time + 1; // decelerating to zero velocity | |
if ( type == 'easeInOutCubic' ) return time < 0.5 ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1; // acceleration until halfway, then deceleration | |
if ( type == 'easeInQuart' ) return time * time * time * time; // accelerating from zero velocity | |
if ( type == 'easeOutQuart' ) return 1 - (--time) * time * time * time; // decelerating to zero velocity | |
if ( type == 'easeInOutQuart' ) return time < 0.5 ? 8 * time * time * time * time : 1 - 8 * (--time) * time * time * time; // acceleration until halfway, then deceleration | |
if ( type == 'easeInQuint' ) return time * time * time * time * time; // accelerating from zero velocity | |
if ( type == 'easeOutQuint' ) return 1 + (--time) * time * time * time * time; // decelerating to zero velocity | |
if ( type == 'easeInOutQuint' ) return time < 0.5 ? 16 * time * time * time * time * time : 1 + 16 * (--time) * time * time * time * time; // acceleration until halfway, then deceleration | |
return time; // no easing, no acceleration | |
}; | |
// Update the URL | |
var updateURL = function (url, anchor) { | |
if ( url === 'true' && history.pushState ) { | |
history.pushState( {pos:anchor.id}, '', '#' + anchor.id ); | |
} | |
}; | |
// Calculate how far to scroll | |
var getEndLocation = function (anchor) { | |
var location = 0; | |
if (anchor.offsetParent) { | |
do { | |
location += anchor.offsetTop; | |
anchor = anchor.offsetParent; | |
} while (anchor); | |
} | |
location = location - headerHeight; | |
if ( location >= 0 ) { | |
return location; | |
} else { | |
return 0; | |
} | |
}; | |
var endLocation = getEndLocation(anchor); | |
var distance = endLocation - startLocation; | |
// Stop the scrolling animation when the anchor is reached (or at the top/bottom of the page) | |
var stopAnimation = function () { | |
var currentLocation = window.pageYOffset; | |
if ( position == endLocation || currentLocation == endLocation || ( (window.innerHeight + currentLocation) >= document.body.scrollHeight ) ) { | |
clearInterval(runAnimation); | |
} | |
}; | |
// Scroll the page by an increment, and check if it's time to stop | |
var animateScroll = function () { | |
timeLapsed += 16; | |
percentage = ( timeLapsed / duration ); | |
percentage = ( percentage > 1 ) ? 1 : percentage; | |
position = startLocation + ( distance * easingPattern(easing, percentage) ); | |
window.scrollTo( 0, position ); | |
stopAnimation(); | |
}; | |
// EVENTS, LISTENERS, AND INITS | |
updateURL(url, anchor); | |
var runAnimation = setInterval(animateScroll, 16); | |
}; | |
// Check that anchor exists and run scroll animation | |
var handleToggleClick = function (event) { | |
// SELECTORS | |
// Get anchor link and calculate distance from the top | |
var dataID = this.getAttribute('href'); | |
var dataTarget = document.querySelector(dataID); | |
var dataSpeed = this.getAttribute('data-speed'); | |
var dataEasing = this.getAttribute('data-easing'); | |
var dataURL = this.getAttribute('data-url'); | |
// EVENTS, LISTENERS, AND INITS | |
event.preventDefault(); | |
if (dataTarget) { | |
runSmoothScroll( dataTarget, dataSpeed || 500, dataEasing || 'easeInOutCubic', dataURL || 'false' ); | |
} | |
}; | |
// EVENTS, LISTENERS, AND INITS | |
// When a toggle is clicked, run the click handler | |
Array.prototype.forEach.call(scrollToggles, function (toggle, index) { | |
toggle.addEventListener('click', handleToggleClick, false); | |
}); | |
// Return to the top of the page when back button is clicked and no hash is set | |
window.onpopstate = function (event) { | |
if ( event.state === null && window.location.hash === '' ) { | |
window.scrollTo( 0, 0 ); | |
} | |
}; | |
} | |
})(window, document); |