@odopod/odo-reveal
Version:
Reveal elements on the page as they come into view.
197 lines (153 loc) • 5.32 kB
JavaScript
import OdoDevice from '@odopod/odo-device';
import OdoViewport from '@odopod/odo-viewport';
import { getPercentageOption, isNativeAndroid, onTransitionEnd } from '@odopod/odo-helpers';
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
/**
* @fileoverview Fade elements in and up when they come into view. Waits for
* images inside the main element to load before triggering the fade.
*
* @author Glen Cheney <glen@odopod.com>
*
* TODO: Add a (better) way to ignore/add/remove images or videos.
*/
var Reveal = function () {
/**
* @param {Element} element Main element for the module.
* @constructor
*/
function Reveal(element) {
var _this = this;
classCallCheck(this, Reveal);
this.element = element;
this.images = this._getDependentImages();
this._numImages = this.images.length;
this._loadedImages = 0;
this._imageCompleteHandler = this._imageComplete.bind(this);
this._ready = new Promise(function (resolve) {
_this._resolve = resolve;
});
if (this._numImages > 0) {
this.images.forEach(function (image) {
image.addEventListener('load', _this._imageCompleteHandler);
image.addEventListener('error', _this._imageCompleteHandler);
});
} else {
this._resolve();
}
// To avoid blank text while scrolling on native Android,
// the type is faded in immediately and not given to the viewport
// to track.
if (Reveal.HAS_SCROLL_ANIMATION) {
/**
* Viewport id to remove it later.
* @type {string}
*/
this.id = OdoViewport.add({
element: element,
threshold: getPercentageOption(element.getAttribute('data-threshold'), '25%'),
enter: this._enteredView.bind(this)
});
} else {
this._show();
}
}
/**
* Get all images within the main element which do not have the "ignore" class
* on a parent element.
* @return {Array.<HTMLImageElement>}
* @private
*/
Reveal.prototype._getDependentImages = function _getDependentImages() {
var images = Array.from(this.element.querySelectorAll('img'));
return images.filter(function (img) {
return img.closest('.' + Reveal.ClassName.IGNORE) === null;
});
};
/**
* Element is in view. Wait until all images have finished loading then animate
* the targets in.
*/
Reveal.prototype._enteredView = function _enteredView() {
this._ready.then(this._show.bind(this));
};
/**
* Main element came into view. Add the fade in class to the main element which
* triggers all type to start transitioning.
* @private
*/
Reveal.prototype._show = function _show() {
var _this2 = this;
this.element.classList.add(Reveal.ClassName.IN);
var targetSelector = '.' + Reveal.ClassName.TARGET;
var targets = Array.from(this.element.querySelectorAll(targetSelector));
// Listen transition end on each target and add a class which removes
// the transform and layer promotion from it.
targets.forEach(function (el) {
onTransitionEnd(el, _this2._handleShown, null, OdoDevice.Dom.TRANSFORM);
});
this.dispose();
};
/**
* Add the done class which removes the transforms and layer promotion.
* @private
*/
Reveal.prototype._handleShown = function _handleShown(evt) {
evt.target.classList.add(Reveal.ClassName.DONE);
};
/**
* An image loaded or failed to load. If it was the last image, resolve the
* promise waiting for all images to finish.
* @private
*/
Reveal.prototype._imageComplete = function _imageComplete() {
this._loadedImages += 1;
if (this._loadedImages === this._numImages) {
this._resolve();
}
};
/**
* Remove the type animations from the viewport watcher. Has no effect if the
* element has already come into view.
*/
Reveal.prototype.dispose = function dispose() {
var _this3 = this;
OdoViewport.remove(this.id);
this.images.forEach(function (image) {
image.removeEventListener('load', _this3._imageCompleteHandler);
image.removeEventListener('error', _this3._imageCompleteHandler);
});
this.images = null;
this.element = null;
};
/**
* Auto-initialize all odo reveal elements currently on the page.
* @param {Element|Document} [context] Optional context to initialize elements within.
* @return {Array.<Reveal>}
*/
Reveal.initializeAll = function initializeAll() {
var context = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document;
return Array.from(context.querySelectorAll('.odo-reveal'), function (element) {
return new Reveal(element);
});
};
return Reveal;
}();
/**
* Whether or not to add the main element to the Viewport watcher. By default,
* no native Android browsers are registered. The type will fade in immediately.
* @type {boolean}
*/
Reveal.HAS_SCROLL_ANIMATION = !isNativeAndroid(navigator.userAgent);
/** @enum {string} */
Reveal.ClassName = {
TARGET: 'odo-reveal__target',
IN: 'odo-reveal--shown',
DONE: 'odo-reveal--done',
IGNORE: 'odo-reveal__ignore'
};
export default Reveal;
//# sourceMappingURL=odo-reveal.esm.js.map