import { preloadImage } from './utils'; /** * Observe images that doesn't match selector. * * @class * @classdesc If the image gets within 50px in the Y axis, start the download. */ export default class LazyLoadImages { constructor() { const images = document.getElementsByClassName('.js-lazy-image'); const config = { rootMargin: '50px 0px', threshold: LazyLoadImages.THRESHOLD }; if(!LazyLoadImages.SUPPORTS_INTERSECTION_OBSERVER) { this._loadImagesImmediately(images); return; } this._count = images.length; this._onIntersection = this._onIntersection.bind(this); this._observer = new IntersectionObserver(this._onIntersection, config); [...images].forEach(img => { if(img.classList.contains(LazyLoadImages.HANDLED_CLASS)) { return; } this._observer.observe(img); }); } // check browser support static get SUPPORTS_INTERSECTION_OBSERVER() { return 'IntersectionObserver' in window; } // class with which to select images static get HANDLED_CLASS() { return 'js-lazy-image--handled'; } // what % of visiblity of the target the observer should trigger static get THRESHOLD() { return 0.01; } // initialize new LazyLoadImages loader static init() { if(this._instance) { this._instance._disconnect(); } this._count = 0; this._instance = new LazyLoadImages(); } // disconnect LazyLoadImages loader // used if new LazyLoadImages() was called _disconnect() { if(!this._observer) { return; } this._observer.disconnect(); } _onIntersection(entries) { entries.forEach(entry => { if(entry.intersectionRatio < 0) { return; } this._count--; this._observer.unobserve(entry.target); this._preloadImage(entry.target); }); if(this._count > 0) { return; } this._observer.disconnect(); } /** * Preloading image. * * @param {String} img - image to preload. */ _preloadImage(img) { const src = img.dataset.src; if(!src) { return; } return preloadImage(src).then(() => this._applyImage(img, src)); } /** * If IntersectionObserver API is not supported, load all images. * * @param {Array} images - images to add on the page. */ _loadImagesImmediately(images) { [...images].forEach(image => this._preloadImage(image)); } /** * Adding image on the page. * * @param {HTMLElement} _img - image to add on the page. * @param {String} src - image source path. */ _applyImage(_img, src) { const el = _img.querySelector('.js-lazy-image-content'); if(!el) { return; } // Prevent this from being lazy loaded a second time. _img.classList.add(LazyLoadImages.HANDLED_CLASS); el.style.backgroundImage = `url(${src})`; el.classList.add('fade-in'); } }