mirror of
https://github.com/byReqz/glitch-landingpage.git
synced 2024-11-23 23:04:55 +00:00
749 lines
21 KiB
JavaScript
749 lines
21 KiB
JavaScript
|
//
|
||
|
// SmoothScroll for websites v1.4.5 (Balazs Galambosi)
|
||
|
// http://www.smoothscroll.net/
|
||
|
//
|
||
|
// Licensed under the terms of the MIT license.
|
||
|
//
|
||
|
// You may use it in your theme if you credit me.
|
||
|
// It is also free to use on any individual website.
|
||
|
//
|
||
|
// Exception:
|
||
|
// The only restriction is to not publish any
|
||
|
// extension for browsers or native application
|
||
|
// without getting a written permission first.
|
||
|
//
|
||
|
|
||
|
(function () {
|
||
|
|
||
|
// Scroll Variables (tweakable)
|
||
|
var defaultOptions = {
|
||
|
|
||
|
// Scrolling Core
|
||
|
frameRate : 150, // [Hz]
|
||
|
animationTime : 400, // [ms]
|
||
|
stepSize : 100, // [px]
|
||
|
|
||
|
// Pulse (less tweakable)
|
||
|
// ratio of "tail" to "acceleration"
|
||
|
pulseAlgorithm : true,
|
||
|
pulseScale : 4,
|
||
|
pulseNormalize : 1,
|
||
|
|
||
|
// Acceleration
|
||
|
accelerationDelta : 50, // 50
|
||
|
accelerationMax : 3, // 3
|
||
|
|
||
|
// Keyboard Settings
|
||
|
keyboardSupport : true, // option
|
||
|
arrowScroll : 50, // [px]
|
||
|
|
||
|
// Other
|
||
|
touchpadSupport : false, // ignore touchpad by default
|
||
|
fixedBackground : true,
|
||
|
excluded : ''
|
||
|
};
|
||
|
|
||
|
var options = defaultOptions;
|
||
|
|
||
|
|
||
|
// Other Variables
|
||
|
var isExcluded = false;
|
||
|
var isFrame = false;
|
||
|
var direction = { x: 0, y: 0 };
|
||
|
var initDone = false;
|
||
|
var root = document.documentElement;
|
||
|
var activeElement;
|
||
|
var observer;
|
||
|
var refreshSize;
|
||
|
var deltaBuffer = [];
|
||
|
var isMac = /^Mac/.test(navigator.platform);
|
||
|
|
||
|
var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32,
|
||
|
pageup: 33, pagedown: 34, end: 35, home: 36 };
|
||
|
var arrowKeys = { 37: 1, 38: 1, 39: 1, 40: 1 };
|
||
|
|
||
|
/***********************************************
|
||
|
* INITIALIZE
|
||
|
***********************************************/
|
||
|
|
||
|
/**
|
||
|
* Tests if smooth scrolling is allowed. Shuts down everything if not.
|
||
|
*/
|
||
|
function initTest() {
|
||
|
if (options.keyboardSupport) {
|
||
|
addEvent('keydown', keydown);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets up scrolls array, determines if frames are involved.
|
||
|
*/
|
||
|
function init() {
|
||
|
|
||
|
if (initDone || !document.body) return;
|
||
|
|
||
|
initDone = true;
|
||
|
|
||
|
var body = document.body;
|
||
|
var html = document.documentElement;
|
||
|
var windowHeight = window.innerHeight;
|
||
|
var scrollHeight = body.scrollHeight;
|
||
|
|
||
|
// check compat mode for root element
|
||
|
root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
|
||
|
activeElement = body;
|
||
|
|
||
|
initTest();
|
||
|
|
||
|
// Checks if this script is running in a frame
|
||
|
if (top != self) {
|
||
|
isFrame = true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Safari 10 fixed it, Chrome fixed it in v45:
|
||
|
* This fixes a bug where the areas left and right to
|
||
|
* the content does not trigger the onmousewheel event
|
||
|
* on some pages. e.g.: html, body { height: 100% }
|
||
|
*/
|
||
|
else if (isOldSafari &&
|
||
|
scrollHeight > windowHeight &&
|
||
|
(body.offsetHeight <= windowHeight ||
|
||
|
html.offsetHeight <= windowHeight)) {
|
||
|
|
||
|
var fullPageElem = document.createElement('div');
|
||
|
fullPageElem.style.cssText = 'position:absolute; z-index:-10000; ' +
|
||
|
'top:0; left:0; right:0; height:' +
|
||
|
root.scrollHeight + 'px';
|
||
|
document.body.appendChild(fullPageElem);
|
||
|
|
||
|
// DOM changed (throttled) to fix height
|
||
|
var pendingRefresh;
|
||
|
refreshSize = function () {
|
||
|
if (pendingRefresh) return; // could also be: clearTimeout(pendingRefresh);
|
||
|
pendingRefresh = setTimeout(function () {
|
||
|
if (isExcluded) return; // could be running after cleanup
|
||
|
fullPageElem.style.height = '0';
|
||
|
fullPageElem.style.height = root.scrollHeight + 'px';
|
||
|
pendingRefresh = null;
|
||
|
}, 500); // act rarely to stay fast
|
||
|
};
|
||
|
|
||
|
setTimeout(refreshSize, 10);
|
||
|
|
||
|
addEvent('resize', refreshSize);
|
||
|
|
||
|
// TODO: attributeFilter?
|
||
|
var config = {
|
||
|
attributes: true,
|
||
|
childList: true,
|
||
|
characterData: false
|
||
|
// subtree: true
|
||
|
};
|
||
|
|
||
|
observer = new MutationObserver(refreshSize);
|
||
|
observer.observe(body, config);
|
||
|
|
||
|
if (root.offsetHeight <= windowHeight) {
|
||
|
var clearfix = document.createElement('div');
|
||
|
clearfix.style.clear = 'both';
|
||
|
body.appendChild(clearfix);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// disable fixed background
|
||
|
if (!options.fixedBackground && !isExcluded) {
|
||
|
body.style.backgroundAttachment = 'scroll';
|
||
|
html.style.backgroundAttachment = 'scroll';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Removes event listeners and other traces left on the page.
|
||
|
*/
|
||
|
function cleanup() {
|
||
|
observer && observer.disconnect();
|
||
|
removeEvent(wheelEvent, wheel);
|
||
|
removeEvent('mousedown', mousedown);
|
||
|
removeEvent('keydown', keydown);
|
||
|
removeEvent('resize', refreshSize);
|
||
|
removeEvent('load', init);
|
||
|
}
|
||
|
|
||
|
|
||
|
/************************************************
|
||
|
* SCROLLING
|
||
|
************************************************/
|
||
|
|
||
|
var que = [];
|
||
|
var pending = false;
|
||
|
var lastScroll = Date.now();
|
||
|
|
||
|
/**
|
||
|
* Pushes scroll actions to the scrolling queue.
|
||
|
*/
|
||
|
function scrollArray(elem, left, top) {
|
||
|
|
||
|
directionCheck(left, top);
|
||
|
|
||
|
if (options.accelerationMax != 1) {
|
||
|
var now = Date.now();
|
||
|
var elapsed = now - lastScroll;
|
||
|
if (elapsed < options.accelerationDelta) {
|
||
|
var factor = (1 + (50 / elapsed)) / 2;
|
||
|
if (factor > 1) {
|
||
|
factor = Math.min(factor, options.accelerationMax);
|
||
|
left *= factor;
|
||
|
top *= factor;
|
||
|
}
|
||
|
}
|
||
|
lastScroll = Date.now();
|
||
|
}
|
||
|
|
||
|
// push a scroll command
|
||
|
que.push({
|
||
|
x: left,
|
||
|
y: top,
|
||
|
lastX: (left < 0) ? 0.99 : -0.99,
|
||
|
lastY: (top < 0) ? 0.99 : -0.99,
|
||
|
start: Date.now()
|
||
|
});
|
||
|
|
||
|
// don't act if there's a pending queue
|
||
|
if (pending) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var scrollWindow = (elem === document.body);
|
||
|
|
||
|
var step = function (time) {
|
||
|
|
||
|
var now = Date.now();
|
||
|
var scrollX = 0;
|
||
|
var scrollY = 0;
|
||
|
|
||
|
for (var i = 0; i < que.length; i++) {
|
||
|
|
||
|
var item = que[i];
|
||
|
var elapsed = now - item.start;
|
||
|
var finished = (elapsed >= options.animationTime);
|
||
|
|
||
|
// scroll position: [0, 1]
|
||
|
var position = (finished) ? 1 : elapsed / options.animationTime;
|
||
|
|
||
|
// easing [optional]
|
||
|
if (options.pulseAlgorithm) {
|
||
|
position = pulse(position);
|
||
|
}
|
||
|
|
||
|
// only need the difference
|
||
|
var x = (item.x * position - item.lastX) >> 0;
|
||
|
var y = (item.y * position - item.lastY) >> 0;
|
||
|
|
||
|
// add this to the total scrolling
|
||
|
scrollX += x;
|
||
|
scrollY += y;
|
||
|
|
||
|
// update last values
|
||
|
item.lastX += x;
|
||
|
item.lastY += y;
|
||
|
|
||
|
// delete and step back if it's over
|
||
|
if (finished) {
|
||
|
que.splice(i, 1); i--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// scroll left and top
|
||
|
if (scrollWindow) {
|
||
|
window.scrollBy(scrollX, scrollY);
|
||
|
}
|
||
|
else {
|
||
|
if (scrollX) elem.scrollLeft += scrollX;
|
||
|
if (scrollY) elem.scrollTop += scrollY;
|
||
|
}
|
||
|
|
||
|
// clean up if there's nothing left to do
|
||
|
if (!left && !top) {
|
||
|
que = [];
|
||
|
}
|
||
|
|
||
|
if (que.length) {
|
||
|
requestFrame(step, elem, (1000 / options.frameRate + 1));
|
||
|
} else {
|
||
|
pending = false;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// start a new queue of actions
|
||
|
requestFrame(step, elem, 0);
|
||
|
pending = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************
|
||
|
* EVENTS
|
||
|
***********************************************/
|
||
|
|
||
|
/**
|
||
|
* Mouse wheel handler.
|
||
|
* @param {Object} event
|
||
|
*/
|
||
|
function wheel(event) {
|
||
|
|
||
|
if (!initDone) {
|
||
|
init();
|
||
|
}
|
||
|
|
||
|
var target = event.target;
|
||
|
|
||
|
// leave early if default action is prevented
|
||
|
// or it's a zooming event with CTRL
|
||
|
if (event.defaultPrevented || event.ctrlKey) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// leave embedded content alone (flash & pdf)
|
||
|
if (isNodeName(activeElement, 'embed') ||
|
||
|
(isNodeName(target, 'embed') && /\.pdf/i.test(target.src)) ||
|
||
|
isNodeName(activeElement, 'object') ||
|
||
|
target.shadowRoot) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
var deltaX = -event.wheelDeltaX || event.deltaX || 0;
|
||
|
var deltaY = -event.wheelDeltaY || event.deltaY || 0;
|
||
|
|
||
|
if (isMac) {
|
||
|
if (event.wheelDeltaX && isDivisible(event.wheelDeltaX, 120)) {
|
||
|
deltaX = -120 * (event.wheelDeltaX / Math.abs(event.wheelDeltaX));
|
||
|
}
|
||
|
if (event.wheelDeltaY && isDivisible(event.wheelDeltaY, 120)) {
|
||
|
deltaY = -120 * (event.wheelDeltaY / Math.abs(event.wheelDeltaY));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// use wheelDelta if deltaX/Y is not available
|
||
|
if (!deltaX && !deltaY) {
|
||
|
deltaY = -event.wheelDelta || 0;
|
||
|
}
|
||
|
|
||
|
// line based scrolling (Firefox mostly)
|
||
|
if (event.deltaMode === 1) {
|
||
|
deltaX *= 40;
|
||
|
deltaY *= 40;
|
||
|
}
|
||
|
|
||
|
var overflowing = overflowingAncestor(target);
|
||
|
|
||
|
// nothing to do if there's no element that's scrollable
|
||
|
if (!overflowing) {
|
||
|
// except Chrome iframes seem to eat wheel events, which we need to
|
||
|
// propagate up, if the iframe has nothing overflowing to scroll
|
||
|
if (isFrame && isChrome) {
|
||
|
// change target to iframe element itself for the parent frame
|
||
|
Object.defineProperty(event, "target", {value: window.frameElement});
|
||
|
return parent.wheel(event);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// check if it's a touchpad scroll that should be ignored
|
||
|
if (!options.touchpadSupport && isTouchpad(deltaY)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// scale by step size
|
||
|
// delta is 120 most of the time
|
||
|
// synaptics seems to send 1 sometimes
|
||
|
if (Math.abs(deltaX) > 1.2) {
|
||
|
deltaX *= options.stepSize / 120;
|
||
|
}
|
||
|
if (Math.abs(deltaY) > 1.2) {
|
||
|
deltaY *= options.stepSize / 120;
|
||
|
}
|
||
|
|
||
|
scrollArray(overflowing, deltaX, deltaY);
|
||
|
event.preventDefault();
|
||
|
scheduleClearCache();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Keydown event handler.
|
||
|
* @param {Object} event
|
||
|
*/
|
||
|
function keydown(event) {
|
||
|
|
||
|
var target = event.target;
|
||
|
var modifier = event.ctrlKey || event.altKey || event.metaKey ||
|
||
|
(event.shiftKey && event.keyCode !== key.spacebar);
|
||
|
|
||
|
// our own tracked active element could've been removed from the DOM
|
||
|
if (!document.body.contains(activeElement)) {
|
||
|
activeElement = document.activeElement;
|
||
|
}
|
||
|
|
||
|
// do nothing if user is editing text
|
||
|
// or using a modifier key (except shift)
|
||
|
// or in a dropdown
|
||
|
// or inside interactive elements
|
||
|
var inputNodeNames = /^(textarea|select|embed|object)$/i;
|
||
|
var buttonTypes = /^(button|submit|radio|checkbox|file|color|image)$/i;
|
||
|
if ( event.defaultPrevented ||
|
||
|
inputNodeNames.test(target.nodeName) ||
|
||
|
isNodeName(target, 'input') && !buttonTypes.test(target.type) ||
|
||
|
isNodeName(activeElement, 'video') ||
|
||
|
isInsideYoutubeVideo(event) ||
|
||
|
target.isContentEditable ||
|
||
|
modifier ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// [spacebar] should trigger button press, leave it alone
|
||
|
if ((isNodeName(target, 'button') ||
|
||
|
isNodeName(target, 'input') && buttonTypes.test(target.type)) &&
|
||
|
event.keyCode === key.spacebar) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// [arrwow keys] on radio buttons should be left alone
|
||
|
if (isNodeName(target, 'input') && target.type == 'radio' &&
|
||
|
arrowKeys[event.keyCode]) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
var shift, x = 0, y = 0;
|
||
|
var overflowing = overflowingAncestor(activeElement);
|
||
|
|
||
|
if (!overflowing) {
|
||
|
// Chrome iframes seem to eat key events, which we need to
|
||
|
// propagate up, if the iframe has nothing overflowing to scroll
|
||
|
return (isFrame && isChrome) ? parent.keydown(event) : true;
|
||
|
}
|
||
|
|
||
|
var clientHeight = overflowing.clientHeight;
|
||
|
|
||
|
if (overflowing == document.body) {
|
||
|
clientHeight = window.innerHeight;
|
||
|
}
|
||
|
|
||
|
switch (event.keyCode) {
|
||
|
case key.up:
|
||
|
y = -options.arrowScroll;
|
||
|
break;
|
||
|
case key.down:
|
||
|
y = options.arrowScroll;
|
||
|
break;
|
||
|
case key.spacebar: // (+ shift)
|
||
|
shift = event.shiftKey ? 1 : -1;
|
||
|
y = -shift * clientHeight * 0.9;
|
||
|
break;
|
||
|
case key.pageup:
|
||
|
y = -clientHeight * 0.9;
|
||
|
break;
|
||
|
case key.pagedown:
|
||
|
y = clientHeight * 0.9;
|
||
|
break;
|
||
|
case key.home:
|
||
|
y = -overflowing.scrollTop;
|
||
|
break;
|
||
|
case key.end:
|
||
|
var scroll = overflowing.scrollHeight - overflowing.scrollTop;
|
||
|
var scrollRemaining = scroll - clientHeight;
|
||
|
y = (scrollRemaining > 0) ? scrollRemaining + 10 : 0;
|
||
|
break;
|
||
|
case key.left:
|
||
|
x = -options.arrowScroll;
|
||
|
break;
|
||
|
case key.right:
|
||
|
x = options.arrowScroll;
|
||
|
break;
|
||
|
default:
|
||
|
return true; // a key we don't care about
|
||
|
}
|
||
|
|
||
|
scrollArray(overflowing, x, y);
|
||
|
event.preventDefault();
|
||
|
scheduleClearCache();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Mousedown event only for updating activeElement
|
||
|
*/
|
||
|
function mousedown(event) {
|
||
|
activeElement = event.target;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************
|
||
|
* OVERFLOW
|
||
|
***********************************************/
|
||
|
|
||
|
var uniqueID = (function () {
|
||
|
var i = 0;
|
||
|
return function (el) {
|
||
|
return el.uniqueID || (el.uniqueID = i++);
|
||
|
};
|
||
|
})();
|
||
|
|
||
|
var cache = {}; // cleared out after a scrolling session
|
||
|
var clearCacheTimer;
|
||
|
|
||
|
//setInterval(function () { cache = {}; }, 10 * 1000);
|
||
|
|
||
|
function scheduleClearCache() {
|
||
|
clearTimeout(clearCacheTimer);
|
||
|
clearCacheTimer = setInterval(function () { cache = {}; }, 1*1000);
|
||
|
}
|
||
|
|
||
|
function setCache(elems, overflowing) {
|
||
|
for (var i = elems.length; i--;)
|
||
|
cache[uniqueID(elems[i])] = overflowing;
|
||
|
return overflowing;
|
||
|
}
|
||
|
|
||
|
// (body) (root)
|
||
|
// | hidden | visible | scroll | auto |
|
||
|
// hidden | no | no | YES | YES |
|
||
|
// visible | no | YES | YES | YES |
|
||
|
// scroll | no | YES | YES | YES |
|
||
|
// auto | no | YES | YES | YES |
|
||
|
|
||
|
function overflowingAncestor(el) {
|
||
|
var elems = [];
|
||
|
var body = document.body;
|
||
|
var rootScrollHeight = root.scrollHeight;
|
||
|
do {
|
||
|
var cached = cache[uniqueID(el)];
|
||
|
if (cached) {
|
||
|
return setCache(elems, cached);
|
||
|
}
|
||
|
elems.push(el);
|
||
|
if (rootScrollHeight === el.scrollHeight) {
|
||
|
var topOverflowsNotHidden = overflowNotHidden(root) && overflowNotHidden(body);
|
||
|
var isOverflowCSS = topOverflowsNotHidden || overflowAutoOrScroll(root);
|
||
|
if (isFrame && isContentOverflowing(root) ||
|
||
|
!isFrame && isOverflowCSS) {
|
||
|
return setCache(elems, getScrollRoot());
|
||
|
}
|
||
|
} else if (isContentOverflowing(el) && overflowAutoOrScroll(el)) {
|
||
|
return setCache(elems, el);
|
||
|
}
|
||
|
} while (el = el.parentElement);
|
||
|
}
|
||
|
|
||
|
function isContentOverflowing(el) {
|
||
|
return (el.clientHeight + 10 < el.scrollHeight);
|
||
|
}
|
||
|
|
||
|
// typically for <body> and <html>
|
||
|
function overflowNotHidden(el) {
|
||
|
var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
|
||
|
return (overflow !== 'hidden');
|
||
|
}
|
||
|
|
||
|
// for all other elements
|
||
|
function overflowAutoOrScroll(el) {
|
||
|
var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y');
|
||
|
return (overflow === 'scroll' || overflow === 'auto');
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************
|
||
|
* HELPERS
|
||
|
***********************************************/
|
||
|
|
||
|
function addEvent(type, fn) {
|
||
|
window.addEventListener(type, fn, false);
|
||
|
}
|
||
|
|
||
|
function removeEvent(type, fn) {
|
||
|
window.removeEventListener(type, fn, false);
|
||
|
}
|
||
|
|
||
|
function isNodeName(el, tag) {
|
||
|
return (el.nodeName||'').toLowerCase() === tag.toLowerCase();
|
||
|
}
|
||
|
|
||
|
function directionCheck(x, y) {
|
||
|
x = (x > 0) ? 1 : -1;
|
||
|
y = (y > 0) ? 1 : -1;
|
||
|
if (direction.x !== x || direction.y !== y) {
|
||
|
direction.x = x;
|
||
|
direction.y = y;
|
||
|
que = [];
|
||
|
lastScroll = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var deltaBufferTimer;
|
||
|
|
||
|
if (window.localStorage && localStorage.SS_deltaBuffer) {
|
||
|
try { // #46 Safari throws in private browsing for localStorage
|
||
|
deltaBuffer = localStorage.SS_deltaBuffer.split(',');
|
||
|
} catch (e) { }
|
||
|
}
|
||
|
|
||
|
function isTouchpad(deltaY) {
|
||
|
if (!deltaY) return;
|
||
|
if (!deltaBuffer.length) {
|
||
|
deltaBuffer = [deltaY, deltaY, deltaY];
|
||
|
}
|
||
|
deltaY = Math.abs(deltaY);
|
||
|
deltaBuffer.push(deltaY);
|
||
|
deltaBuffer.shift();
|
||
|
clearTimeout(deltaBufferTimer);
|
||
|
deltaBufferTimer = setTimeout(function () {
|
||
|
try { // #46 Safari throws in private browsing for localStorage
|
||
|
localStorage.SS_deltaBuffer = deltaBuffer.join(',');
|
||
|
} catch (e) { }
|
||
|
}, 1000);
|
||
|
return !allDeltasDivisableBy(120) && !allDeltasDivisableBy(100);
|
||
|
}
|
||
|
|
||
|
function isDivisible(n, divisor) {
|
||
|
return (Math.floor(n / divisor) == n / divisor);
|
||
|
}
|
||
|
|
||
|
function allDeltasDivisableBy(divisor) {
|
||
|
return (isDivisible(deltaBuffer[0], divisor) &&
|
||
|
isDivisible(deltaBuffer[1], divisor) &&
|
||
|
isDivisible(deltaBuffer[2], divisor));
|
||
|
}
|
||
|
|
||
|
function isInsideYoutubeVideo(event) {
|
||
|
var elem = event.target;
|
||
|
var isControl = false;
|
||
|
if (document.URL.indexOf ('www.youtube.com/watch') != -1) {
|
||
|
do {
|
||
|
isControl = (elem.classList &&
|
||
|
elem.classList.contains('html5-video-controls'));
|
||
|
if (isControl) break;
|
||
|
} while (elem = elem.parentNode);
|
||
|
}
|
||
|
return isControl;
|
||
|
}
|
||
|
|
||
|
var requestFrame = (function () {
|
||
|
return (window.requestAnimationFrame ||
|
||
|
window.webkitRequestAnimationFrame ||
|
||
|
window.mozRequestAnimationFrame ||
|
||
|
function (callback, element, delay) {
|
||
|
window.setTimeout(callback, delay || (1000/60));
|
||
|
});
|
||
|
})();
|
||
|
|
||
|
var MutationObserver = (window.MutationObserver ||
|
||
|
window.WebKitMutationObserver ||
|
||
|
window.MozMutationObserver);
|
||
|
|
||
|
var getScrollRoot = (function() {
|
||
|
var SCROLL_ROOT;
|
||
|
return function() {
|
||
|
if (!SCROLL_ROOT) {
|
||
|
var dummy = document.createElement('div');
|
||
|
dummy.style.cssText = 'height:10000px;width:1px;';
|
||
|
document.body.appendChild(dummy);
|
||
|
var bodyScrollTop = document.body.scrollTop;
|
||
|
var docElScrollTop = document.documentElement.scrollTop;
|
||
|
window.scrollBy(0, 3);
|
||
|
if (document.body.scrollTop != bodyScrollTop)
|
||
|
(SCROLL_ROOT = document.body);
|
||
|
else
|
||
|
(SCROLL_ROOT = document.documentElement);
|
||
|
window.scrollBy(0, -3);
|
||
|
document.body.removeChild(dummy);
|
||
|
}
|
||
|
return SCROLL_ROOT;
|
||
|
};
|
||
|
})();
|
||
|
|
||
|
|
||
|
/***********************************************
|
||
|
* PULSE (by Michael Herf)
|
||
|
***********************************************/
|
||
|
|
||
|
/**
|
||
|
* Viscous fluid with a pulse for part and decay for the rest.
|
||
|
* - Applies a fixed force over an interval (a damped acceleration), and
|
||
|
* - Lets the exponential bleed away the velocity over a longer interval
|
||
|
* - Michael Herf, http://stereopsis.com/stopping/
|
||
|
*/
|
||
|
function pulse_(x) {
|
||
|
var val, start, expx;
|
||
|
// test
|
||
|
x = x * options.pulseScale;
|
||
|
if (x < 1) { // acceleartion
|
||
|
val = x - (1 - Math.exp(-x));
|
||
|
} else { // tail
|
||
|
// the previous animation ended here:
|
||
|
start = Math.exp(-1);
|
||
|
// simple viscous drag
|
||
|
x -= 1;
|
||
|
expx = 1 - Math.exp(-x);
|
||
|
val = start + (expx * (1 - start));
|
||
|
}
|
||
|
return val * options.pulseNormalize;
|
||
|
}
|
||
|
|
||
|
function pulse(x) {
|
||
|
if (x >= 1) return 1;
|
||
|
if (x <= 0) return 0;
|
||
|
|
||
|
if (options.pulseNormalize == 1) {
|
||
|
options.pulseNormalize /= pulse_(1);
|
||
|
}
|
||
|
return pulse_(x);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************
|
||
|
* FIRST RUN
|
||
|
***********************************************/
|
||
|
|
||
|
var userAgent = window.navigator.userAgent;
|
||
|
var isEdge = /Edge/.test(userAgent); // thank you MS
|
||
|
var isChrome = /chrome/i.test(userAgent) && !isEdge;
|
||
|
var isSafari = /safari/i.test(userAgent) && !isEdge;
|
||
|
var isMobile = /mobile/i.test(userAgent);
|
||
|
var isIEWin7 = /Windows NT 6.1/i.test(userAgent) && /rv:11/i.test(userAgent);
|
||
|
var isOldSafari = isSafari && (/Version\/8/i.test(userAgent) || /Version\/9/i.test(userAgent));
|
||
|
var isEnabledForBrowser = (isChrome || isSafari || isIEWin7) && !isMobile;
|
||
|
|
||
|
var wheelEvent;
|
||
|
if ('onwheel' in document.createElement('div'))
|
||
|
wheelEvent = 'wheel';
|
||
|
else if ('onmousewheel' in document.createElement('div'))
|
||
|
wheelEvent = 'mousewheel';
|
||
|
|
||
|
if (wheelEvent && isEnabledForBrowser) {
|
||
|
addEvent(wheelEvent, wheel);
|
||
|
addEvent('mousedown', mousedown);
|
||
|
addEvent('load', init);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************
|
||
|
* PUBLIC INTERFACE
|
||
|
***********************************************/
|
||
|
|
||
|
function SmoothScroll(optionsToSet) {
|
||
|
for (var key in optionsToSet)
|
||
|
if (defaultOptions.hasOwnProperty(key))
|
||
|
options[key] = optionsToSet[key];
|
||
|
}
|
||
|
SmoothScroll.destroy = cleanup;
|
||
|
|
||
|
if (window.SmoothScrollOptions) // async API
|
||
|
SmoothScroll(window.SmoothScrollOptions);
|
||
|
|
||
|
if (typeof define === 'function' && define.amd)
|
||
|
define(function() {
|
||
|
return SmoothScroll;
|
||
|
});
|
||
|
else if ('object' == typeof exports)
|
||
|
module.exports = SmoothScroll;
|
||
|
else
|
||
|
window.SmoothScroll = SmoothScroll;
|
||
|
|
||
|
})();
|