Created
April 14, 2019 08:17
-
-
Save XianYeeXing/50c313d368ef6d56373901fde29fc550 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
/** | |
* USAGE: | |
* const scrollTo = require('../lib/animated-scroll-to'); | |
* scrollTo(window.innerHeight, {speed: 1000, elemrnt: window}); | |
*/ | |
(function() { | |
'use strict'; | |
// desiredOffset - page offset to scroll to | |
// speed - duration of the scroll per 1000px | |
function __ANIMATE_SCROLL_TO(desiredOffset) { | |
var userOptions = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | |
var options = { | |
speed: 500, | |
minDuration: 250, | |
maxDuration: 1500, | |
cancelOnUserAction: true, | |
element: window, | |
horizontal: false, | |
onComplete: undefined, | |
passive: true, | |
offset: 0 | |
}; | |
var optionsKeys = Object.keys(options); | |
// Override default options | |
for (var i = 0; i < optionsKeys.length; i++) { | |
var key = optionsKeys[i]; | |
if (typeof userOptions[key] !== 'undefined') { | |
options[key] = userOptions[key]; | |
} | |
} | |
if (!options.cancelOnUserAction && options.passive) { | |
options.passive = false; | |
if (userOptions.passive) { | |
console && console.warn( | |
'animated-scroll-to:\n "passive" was set to "false" to prevent errors, ' + | |
'as using "cancelOnUserAction: false" doesn\'t work with passive events.') | |
} | |
} | |
if (desiredOffset instanceof HTMLElement) { | |
if (userOptions.element && userOptions.element instanceof HTMLElement) { | |
if (options.horizontal) { | |
desiredOffset = (desiredOffset.getBoundingClientRect().left + userOptions.element.scrollLeft) | |
- userOptions.element.getBoundingClientRect().left; | |
} else { | |
desiredOffset = (desiredOffset.getBoundingClientRect().top + userOptions.element.scrollTop) | |
- userOptions.element.getBoundingClientRect().top; | |
} | |
} else if (options.horizontal) { | |
var scrollLeft = window.scrollX || document.documentElement.scrollLeft; | |
desiredOffset = scrollLeft + desiredOffset.getBoundingClientRect().left; | |
} else { | |
var scrollTop = window.scrollY || document.documentElement.scrollTop; | |
desiredOffset = scrollTop + desiredOffset.getBoundingClientRect().top; | |
} | |
} | |
// Add additonal user offset | |
desiredOffset += options.offset | |
options.isWindow = options.element === window; | |
var initialScrollPosition = null; | |
var initialAxisScollPosition = 0; | |
var maxScroll = null; | |
if (options.isWindow) { | |
if (options.horizontal) { | |
// get cross browser scroll positions | |
initialScrollPosition = window.scrollX || document.documentElement.scrollLeft; | |
initialAxisScollPosition = window.scrollY || document.documentElement.scrollTop; | |
// cross browser document height minus window height | |
maxScroll = Math.max( | |
document.body.scrollWidth, document.documentElement.scrollWidth, | |
document.body.offsetWidth, document.documentElement.offsetWidth, | |
document.body.clientWidth, document.documentElement.clientWidth | |
) - window.innerWidth; | |
} else { | |
// get cross browser scroll positions | |
initialScrollPosition = window.scrollY || document.documentElement.scrollTop; | |
initialAxisScollPosition = window.scrollX || document.documentElement.scrollLeft; | |
// cross browser document width minus window width | |
maxScroll = Math.max( | |
document.body.scrollHeight, document.documentElement.scrollHeight, | |
document.body.offsetHeight, document.documentElement.offsetHeight, | |
document.body.clientHeight, document.documentElement.clientHeight | |
) - window.innerHeight; | |
} | |
} else { | |
// DOM element | |
if (options.horizontal) { | |
initialScrollPosition = options.element.scrollLeft; | |
maxScroll = options.element.scrollWidth - options.element.clientWidth; | |
} else { | |
initialScrollPosition = options.element.scrollTop; | |
maxScroll = options.element.scrollHeight - options.element.clientHeight; | |
} | |
} | |
// If the scroll position is greater than maximum available scroll | |
if (desiredOffset > maxScroll) { | |
desiredOffset = maxScroll; | |
} | |
// Calculate diff to scroll | |
var diff = desiredOffset - initialScrollPosition; | |
// Do nothing if the page is already there | |
if (diff === 0) { | |
// Execute callback if there is any | |
if (options.onComplete && typeof options.onComplete === 'function') { | |
options.onComplete() | |
} | |
return; | |
} | |
// Calculate duration of the scroll | |
var duration = Math.abs(Math.round((diff / 1000) * options.speed)); | |
// Set minimum and maximum duration | |
if (duration < options.minDuration) { | |
duration = options.minDuration; | |
} else if (duration > options.maxDuration) { | |
duration = options.maxDuration; | |
} | |
var startingTime = Date.now(); | |
// Request animation frame ID | |
var requestID = null; | |
// Method handler | |
var handleUserEvent = null; | |
var userEventOptions = { passive: options.passive }; | |
if (options.cancelOnUserAction) { | |
// Set handler to cancel scroll on user action | |
handleUserEvent = function() { | |
removeListeners(); | |
cancelAnimationFrame(requestID); | |
}; | |
window.addEventListener('keydown', handleUserEvent, userEventOptions); | |
window.addEventListener('mousedown', handleUserEvent, userEventOptions); | |
} else { | |
// Set handler to prevent user actions while scroll is active | |
handleUserEvent = function(e) { e.preventDefault(); }; | |
window.addEventListener('scroll', handleUserEvent, userEventOptions); | |
} | |
window.addEventListener('wheel', handleUserEvent, userEventOptions); | |
window.addEventListener('touchstart', handleUserEvent, userEventOptions); | |
var removeListeners = function () { | |
window.removeEventListener('wheel', handleUserEvent, userEventOptions); | |
window.removeEventListener('touchstart', handleUserEvent, userEventOptions); | |
if (options.cancelOnUserAction) { | |
window.removeEventListener('keydown', handleUserEvent, userEventOptions); | |
window.removeEventListener('mousedown', handleUserEvent, userEventOptions); | |
} else { | |
window.removeEventListener('scroll', handleUserEvent, userEventOptions); | |
} | |
}; | |
var step = function () { | |
var timeDiff = Date.now() - startingTime; | |
var t = (timeDiff / duration) - 1; | |
var easing = t * t * t + 1; | |
var scrollPosition = Math.round(initialScrollPosition + (diff * easing)); | |
var doScroll = function(position) { | |
if (options.isWindow) { | |
if (options.horizontal) { | |
options.element.scrollTo(position, initialAxisScollPosition); | |
} else { | |
options.element.scrollTo(initialAxisScollPosition, position); | |
} | |
} else if (options.horizontal) { | |
options.element.scrollLeft = position; | |
} else { | |
options.element.scrollTop = position; | |
} | |
} | |
if (timeDiff < duration && scrollPosition !== desiredOffset) { | |
// If scroll didn't reach desired offset or time is not elapsed | |
// Scroll to a new position | |
// And request a new step | |
doScroll(scrollPosition); | |
requestID = requestAnimationFrame(step); | |
} else { | |
// If the time elapsed or we reached the desired offset | |
// Set scroll to the desired offset (when rounding made it to be off a pixel or two) | |
// Clear animation frame to be sure | |
doScroll(desiredOffset); | |
cancelAnimationFrame(requestID); | |
// Remove listeners | |
removeListeners(); | |
// Animation is complete, execute callback if there is any | |
if (options.onComplete && typeof options.onComplete === 'function') { | |
options.onComplete() | |
} | |
} | |
}; | |
// Start animating scroll | |
requestID = requestAnimationFrame(step); | |
} | |
if (typeof exports !== 'undefined') { | |
if (typeof module !== 'undefined' && module.exports) { | |
module.exports = __ANIMATE_SCROLL_TO; | |
exports = module.exports; | |
} | |
exports.default = __ANIMATE_SCROLL_TO; | |
} else if (window) { | |
window.animateScrollTo = __ANIMATE_SCROLL_TO; | |
} | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment