scroll-scout
Version:
scroll-scout
309 lines (253 loc) • 7.96 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.ScrollScout = factory());
}(this, (function () { 'use strict';
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function unwrapExports (x) {
return x && x.__esModule ? x['default'] : x;
}
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var performanceNow = createCommonjsModule(function (module) {
// Generated by CoffeeScript 1.7.1
(function () {
var getNanoSeconds, hrtime, loadTime;
if (typeof performance !== "undefined" && performance !== null && performance.now) {
module.exports = function () {
return performance.now();
};
} else if (typeof process !== "undefined" && process !== null && process.hrtime) {
module.exports = function () {
return (getNanoSeconds() - loadTime) / 1e6;
};
hrtime = process.hrtime;
getNanoSeconds = function getNanoSeconds() {
var hr;
hr = hrtime();
return hr[0] * 1e9 + hr[1];
};
loadTime = getNanoSeconds();
} else if (Date.now) {
module.exports = function () {
return Date.now() - loadTime;
};
loadTime = Date.now();
} else {
module.exports = function () {
return new Date().getTime() - loadTime;
};
loadTime = new Date().getTime();
}
}).call(commonjsGlobal);
});
var now = performanceNow;
var root = typeof window === 'undefined' ? commonjsGlobal : window;
var vendors = ['moz', 'webkit'];
var suffix = 'AnimationFrame';
var raf = root['request' + suffix];
var caf = root['cancel' + suffix] || root['cancelRequest' + suffix];
for (var i = 0; !raf && i < vendors.length; i++) {
raf = root[vendors[i] + 'Request' + suffix];
caf = root[vendors[i] + 'Cancel' + suffix] || root[vendors[i] + 'CancelRequest' + suffix];
}
// Some versions of FF have rAF but not cAF
if (!raf || !caf) {
var last = 0,
id = 0,
queue = [],
frameDuration = 1000 / 60;
raf = function raf(callback) {
if (queue.length === 0) {
var _now = now(),
next = Math.max(0, frameDuration - (_now - last));
last = next + _now;
setTimeout(function () {
var cp = queue.slice(0);
// Clear queue here to prevent
// callbacks from appending listeners
// to the current frame's queue
queue.length = 0;
for (var i = 0; i < cp.length; i++) {
if (!cp[i].cancelled) {
try {
cp[i].callback(last);
} catch (e) {
setTimeout(function () {
throw e;
}, 0);
}
}
}
}, Math.round(next));
}
queue.push({
handle: ++id,
callback: callback,
cancelled: false
});
return id;
};
caf = function caf(handle) {
for (var i = 0; i < queue.length; i++) {
if (queue[i].handle === handle) {
queue[i].cancelled = true;
}
}
};
}
var index = function (fn) {
// Wrap in a new function to prevent
// `cancel` potentially being assigned
// to the native rAF function
return raf.call(root, fn);
};
var cancel = function () {
caf.apply(root, arguments);
};
var polyfill = function () {
root.requestAnimationFrame = raf;
root.cancelAnimationFrame = caf;
};
index.cancel = cancel;
index.polyfill = polyfill;
var index$1 = createCommonjsModule(function (module, exports) {
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _raf = index;
var _raf2 = _interopRequireDefault(_raf);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
function _toConsumableArray(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
arr2[i] = arr[i];
}return arr2;
} else {
return Array.from(arr);
}
}
var rafThrottle = function rafThrottle(callback) {
var requestId = void 0;
var later = function later(args) {
return function () {
requestId = null;
callback.apply(undefined, _toConsumableArray(args));
};
};
var throttled = function throttled() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
if (requestId == null) {
requestId = (0, _raf2.default)(later(args));
}
};
throttled.cancel = function () {
return _raf2.default.cancel(requestId);
};
return throttled;
};
exports.default = rafThrottle;
});
var throttle = unwrapExports(index$1);
// polyfill requestAnimationFrame when needed
if (!window.requestAnimationFrame) {
index.polyfill();
}
var ScrollScout$1 = new function () {
var windowHeight = void 0,
windowScrollY = void 0;
var watchList = {};
var watchListKey = 0;
var events = ['scroll', 'resize'],
eventHandler = throttle(checkElements);
var eventCallback = function eventCallback() {};
var isScouting = false;
return {
becomesVisible: becomesVisible,
isVisible: isInViewport,
setOnEvent: setOnEvent,
checkElements: checkElements
};
function setOnEvent(callback) {
eventCallback = callback;
}
function updateWindowParams() {
windowHeight = window.innerHeight;
windowScrollY = window.scrollY || window.pageYOffset;
}
function isInViewport(element) {
var threshold = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 50;
if (!isVisible(element)) {
return false;
}
var viewTop = windowScrollY;
var viewBot = viewTop + windowHeight;
var elementTop = getOffset(element);
var elementBot = elementTop + element.offsetHeight;
var offset = threshold / 100 * windowHeight;
return elementBot >= viewTop - offset && elementTop <= viewBot + offset;
}
function becomesVisible(el) {
var threshold = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 50;
return new Promise(function (resolve) {
updateWindowParams();
if (isInViewport(el, threshold)) {
resolve(el);
} else {
addToWatchlist({
element: el,
threshold: threshold,
resolve: resolve
});
scout(true);
}
});
}
function addToWatchlist(obj) {
watchListKey++;
watchList[watchListKey] = obj;
}
function scout(on) {
var action = void 0;
if (on) {
action = isScouting ? false : 'addEventListener';
isScouting = true;
} else {
action = 'removeEventListener';
isScouting = false;
}
if (action) {
events.forEach(function (event) {
return window[action](event, eventHandler);
});
}
}
function getOffset(element) {
return element.getBoundingClientRect().top + windowScrollY;
}
function checkElements() {
eventCallback();
updateWindowParams();
Object.keys(watchList).forEach(function (key) {
if (isInViewport(watchList[key].element, watchList[key].threshold)) {
watchList[key].resolve(watchList[key].element);
delete watchList[key];
}
});
if (Object.keys(watchList).length === 0) {
scout(false);
}
}
function isVisible(someElement) {
// see https://davidwalsh.name/offsetheight-visibility
return someElement.offsetHeight > 0;
}
}();
return ScrollScout$1;
})));