UNPKG

@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
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 };