@exadel/esl
Version:
Exadel Smart Library (ESL) is the lightweight custom elements library that provide a set of super-flexible components
125 lines (124 loc) • 5.25 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var ESLAnimateService_1;
import { wrap } from '../../esl-utils/misc/array';
import { debounce } from '../../esl-utils/async/debounce';
import { memoize, bind } from '../../esl-utils/decorators';
import { ExportNs } from '../../esl-utils/environment/export-ns';
import { CSSClassUtils } from '../../esl-utils/dom/class';
/** Service to animate elements on viewport intersection */
let ESLAnimateService = ESLAnimateService_1 = class ESLAnimateService {
constructor() {
this._io = new IntersectionObserver(this.onIntersect, ESLAnimateService_1.OPTIONS_OBSERVER);
this._entries = new Set();
this._configMap = new WeakMap();
this.deferredOnAnimate = debounce(() => this.onAnimate(), 100);
}
/**
* Subscribe ESlAnimateService on element(s) to animate it on viewport intersection
* @param target - element(s) or elements to observe and animate
* @param config - optional animation configuration
*/
static observe(target, config = {}) {
wrap(target).forEach((item) => this.instance.observe(item, config));
}
/** Unobserve element or elements */
static unobserve(target) {
wrap(target).forEach((item) => this.instance.unobserve(item));
}
/** @returns if service observing target */
static isObserved(target) {
return this.instance.isObserved(target);
}
static get instance() {
return new ESLAnimateService_1();
}
/**
* Subscribe ESlAnimateService on element(s) to animate it on viewport intersection
* @param el - element or elements to observe and animate
* @param config - optional animation configuration
*/
observe(el, config = {}) {
const cfg = Object.assign({}, ESLAnimateService_1.DEFAULT_CONFIG, config);
this._configMap.set(el, cfg);
cfg.force && CSSClassUtils.remove(el, cfg.cls);
this._io.observe(el);
}
/** Unobserve element or elements */
unobserve(el) {
this._io.unobserve(el);
this._configMap.delete(el);
}
/** @returns if service observing target */
isObserved(target) {
return !!this._configMap.get(target);
}
/** Intersection observable callback */
onIntersect(entries) {
entries.forEach(({ target, intersectionRatio, isIntersecting }) => {
const config = this._configMap.get(target);
if (!config)
return;
// Item will be marked as visible in case it intersecting to the viewport with a ratio grater then passed visibleRatio
if (isIntersecting && intersectionRatio >= config.ratio) {
this._entries.add(target);
}
// Item considered as invisible in case it is going to be intersected less then 1% of it's area
if (!isIntersecting && intersectionRatio <= 0.01) {
this._entries.delete(target);
if (config.repeat) {
CSSClassUtils.remove(target, config.cls);
config._timeout && clearTimeout(config._timeout);
}
}
});
this.deferredOnAnimate();
}
/** Process animation query */
onAnimate() {
let time = -1;
this._entries.forEach((target) => {
const config = this._configMap.get(target);
if (!config)
return;
if (config._timeout)
window.clearTimeout(config._timeout);
if (config.group) {
time = time === -1 ? 0 : (time + config.groupDelay);
config._timeout = window.setTimeout(() => this.onAnimateItem(target), time);
}
else {
this.onAnimateItem(target);
}
config._unsubscribe = !config.repeat;
});
}
/** Animates passed item */
onAnimateItem(item) {
const config = this._configMap.get(item);
if (!config)
return;
CSSClassUtils.add(item, config.cls);
this._entries.delete(item);
if (config._unsubscribe)
this.unobserve(item);
}
};
/** ESLAnimateService default animation configuration */
ESLAnimateService.DEFAULT_CONFIG = { cls: 'in', groupDelay: 100, ratio: 0.4 };
/** ESLAnimationService IntersectionObserver properties */
ESLAnimateService.OPTIONS_OBSERVER = { threshold: [0.001, 0.2, 0.4, 0.6, 0.8, 1] };
__decorate([
bind
], ESLAnimateService.prototype, "onIntersect", null);
__decorate([
memoize()
], ESLAnimateService, "instance", null);
ESLAnimateService = ESLAnimateService_1 = __decorate([
ExportNs('AnimateService')
], ESLAnimateService);
export { ESLAnimateService };