UNPKG

photoswipe

Version:
198 lines (168 loc) 5.75 kB
import { getViewportSize, getPanAreaSize } from '../util/viewport-size.js'; import ZoomLevel from './zoom-level.js'; /** @typedef {import('./content.js').default} Content */ /** @typedef {import('./slide.js').default} Slide */ /** @typedef {import('./slide.js').SlideData} SlideData */ /** @typedef {import('../core/base.js').default} PhotoSwipeBase */ /** @typedef {import('../photoswipe.js').default} PhotoSwipe */ /** @typedef {import('../lightbox/lightbox.js').default} PhotoSwipeLightbox */ const MIN_SLIDES_TO_CACHE = 5; /** * Lazy-load an image * This function is used both by Lightbox and PhotoSwipe core, * thus it can be called before dialog is opened. * * @param {SlideData} itemData Data about the slide * @param {PhotoSwipe | PhotoSwipeLightbox | PhotoSwipeBase} instance PhotoSwipe instance * @param {number} index * @returns Image that is being decoded or false. */ export function lazyLoadData(itemData, instance, index) { // src/slide/content/content.js const content = instance.createContentFromData(itemData, index); if (!content || !content.lazyLoad) { return; } const { options } = instance; // We need to know dimensions of the image to preload it, // as it might use srcset and we need to define sizes // @ts-expect-error should provide pswp instance? const viewportSize = instance.viewportSize || getViewportSize(options, instance); const panAreaSize = getPanAreaSize(options, viewportSize, itemData, index); const zoomLevel = new ZoomLevel(options, itemData, -1); zoomLevel.update(content.width, content.height, panAreaSize); content.lazyLoad(); content.setDisplayedSize( Math.ceil(content.width * zoomLevel.initial), Math.ceil(content.height * zoomLevel.initial) ); return content; } /** * Lazy-loads specific slide. * This function is used both by Lightbox and PhotoSwipe core, * thus it can be called before dialog is opened. * * By default it loads image based on viewport size and initial zoom level. * * @param {number} index Slide index * @param {PhotoSwipe | PhotoSwipeLightbox} instance PhotoSwipe or PhotoSwipeLightbox eventable instance */ export function lazyLoadSlide(index, instance) { const itemData = instance.getItemData(index); if (instance.dispatch('lazyLoadSlide', { index, itemData }).defaultPrevented) { return; } return lazyLoadData(itemData, instance, index); } class ContentLoader { /** * @param {PhotoSwipe} pswp */ constructor(pswp) { this.pswp = pswp; // Total amount of cached images this.limit = Math.max( pswp.options.preload[0] + pswp.options.preload[1] + 1, MIN_SLIDES_TO_CACHE ); /** @type {Content[]} */ this._cachedItems = []; } /** * Lazy load nearby slides based on `preload` option. * * @param {number=} diff Difference between slide indexes that was changed recently, or 0. */ updateLazy(diff) { const { pswp } = this; if (pswp.dispatch('lazyLoad').defaultPrevented) { return; } const { preload } = pswp.options; const isForward = diff === undefined ? true : (diff >= 0); let i; // preload[1] - num items to preload in forward direction for (i = 0; i <= preload[1]; i++) { this.loadSlideByIndex(pswp.currIndex + (isForward ? i : (-i))); } // preload[0] - num items to preload in backward direction for (i = 1; i <= preload[0]; i++) { this.loadSlideByIndex(pswp.currIndex + (isForward ? (-i) : i)); } } /** * @param {number} index */ loadSlideByIndex(index) { index = this.pswp.getLoopedIndex(index); // try to get cached content let content = this.getContentByIndex(index); if (!content) { // no cached content, so try to load from scratch: content = lazyLoadSlide(index, this.pswp); // if content can be loaded, add it to cache: if (content) { this.addToCache(content); } } } /** * @param {Slide} slide */ getContentBySlide(slide) { let content = this.getContentByIndex(slide.index); if (!content) { // create content if not found in cache content = this.pswp.createContentFromData(slide.data, slide.index); if (content) { this.addToCache(content); } } if (content) { // assign slide to content content.setSlide(slide); } return content; } /** * @param {Content} content */ addToCache(content) { // move to the end of array this.removeByIndex(content.index); this._cachedItems.push(content); if (this._cachedItems.length > this.limit) { // Destroy the first content that's not attached const indexToRemove = this._cachedItems.findIndex((item) => { return !item.isAttached && !item.hasSlide; }); if (indexToRemove !== -1) { const removedItem = this._cachedItems.splice(indexToRemove, 1)[0]; removedItem.destroy(); } } } /** * Removes an image from cache, does not destroy() it, just removes. * * @param {number} index */ removeByIndex(index) { const indexToRemove = this._cachedItems.findIndex(item => item.index === index); if (indexToRemove !== -1) { this._cachedItems.splice(indexToRemove, 1); } } /** * @param {number} index */ getContentByIndex(index) { return this._cachedItems.find(content => content.index === index); } destroy() { this._cachedItems.forEach(content => content.destroy()); this._cachedItems = null; } } export default ContentLoader;