/
githubmirror
/
amphtml
Обзор
Документация
Войти
/
githubmirror
/
amphtml
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/service/loading-indicator.js
149 строк
3 KB
Kristofer Baxter
♻️ Remove Notice from JS and Markdown (#35717)
19 авг 2021, 21:21
Не верифицирован
19 авг 2021, 21:21
f564b7e
Код
Авторство
О чём код?
import {removeElement} from '#core/dom'; import {createViewportObserver} from '#core/dom/layout/viewport-observer'; import {htmlFor} from '#core/dom/static-template'; import {createLoaderElement} from './loader-element'; import {registerServiceBuilderForDoc} from '../service-helpers'; const MIN_SIZE = 20; /** * @typedef {{ * shown: boolean, * loader: !Element, * container: !Element, * }} */ let LoadingIndicatorStateDef; /** * @param {!Node|!./ampdoc-impl.AmpDoc} nodeOrDoc */ export function installLoadingIndicatorForDoc(nodeOrDoc) { registerServiceBuilderForDoc( nodeOrDoc, 'loadingIndicator', LoadingIndicatorImpl ); } /** * @implements {../service.Disposable} */ export class LoadingIndicatorImpl { /** * @param {!./ampdoc-impl.AmpDoc} ampdoc */ constructor(ampdoc) { /** @private @const */ this.ampdoc_ = ampdoc; const {win} = ampdoc; const inViewport = this.inViewport_.bind(this); const ioCallback = (records) => /** @type {!Array<!IntersectionObserverEntry>} */ (records).forEach( inViewport ); /** @private @const {!IntersectionObserver} */ this.io_ = createViewportObserver(ioCallback, win); /** @private @const {!WeakMap<!AmpElement, !LoadingIndicatorStateDef>} */ this.states_ = new WeakMap(); } /** @override */ dispose() { this.io_.disconnect(); } /** * @param {!AmpElement} element */ track(element) { this.io_.observe(element); } /** * @param {!AmpElement} element */ untrack(element) { this.io_.unobserve(element); this.cleanup_(element); } /** * @param {!IntersectionObserverEntry} record * @private */ inViewport_(record) { const {boundingClientRect, isIntersecting, target} = record; const {height, width} = boundingClientRect; const element = /** @type {!AmpElement} */ (target); const show = isIntersecting && width > MIN_SIZE && height > MIN_SIZE; let state = this.states_.get(element); const isCurrentlyShown = (state && state.shown) || false; if (show === isCurrentlyShown) { // Loading state is the same. return; } if (show && !state) { state = this.createLoaderState_(element, width, height); this.states_.set(element, state); } if (state) { state.shown = show; state.container.classList.toggle('amp-hidden', !show); state.loader.classList.toggle('amp-active', show); } } /** * @param {!AmpElement} element * @param {number} width * @param {number} height * @return {!LoadingIndicatorStateDef} * @private */ createLoaderState_(element, width, height) { const startTime = Date.now(); const loader = createLoaderElement( this.ampdoc_, element, width, height, startTime ); const container = htmlFor(this.ampdoc_.win.document)` <div slot="i-amphtml-svc" class="i-amphtml-svc i-amphtml-loading-container i-amphtml-fill-content amp-hidden"></div> `; container.appendChild(loader); element.appendChild(container); return /** @type {!LoadingIndicatorStateDef} */ ({ shown: false, loader, container, }); } /** * @param {!AmpElement} element * @private */ cleanup_(element) { const state = this.states_.get(element); if (!state) { return; } this.states_.delete(element); removeElement(state.container); } }