UNPKG

photoswipe-deep-zoom-plugin

Version:

A plugin that adds tile-based zoom support to PhotoSwipe. Supports Deepzoom and Zoomify tile formats.

205 lines (168 loc) 4.93 kB
import TileImage, { LOAD_STATE, DECODING_STATE } from './tile-image.js'; import TileImagesCache from './tile-images-cache.js'; import { getTileKey } from './util.js'; class DecodingQueue { constructor(options) { this.images = []; this.maxDecodingCount = options.maxDecodingCount; this.minBatchRequestCount = options.minBatchRequestCount; this.cacheLimit = options.cacheLimit; this._imagesDecodingCount = 0; this.cache = new TileImagesCache(this.cacheLimit); } /** * @param {TileImage} tileImage */ cacheImage(tileImage) { if (tileImage.loadState !== LOAD_STATE.ERROR) { this.cache.add(tileImage); } } hasLoadedImage(x, y, z) { let image = this.getImage(x, y, z); if (image && image.loadState === LOAD_STATE.LOADED) { return true; } return false; } getImage(x, y, z) { let image = this.cache.getByKey( getTileKey(x, y, z) ); if (!image) { image = this.images.find((item) => { return item.x === x && item.y === y && item.z === z; }); } return image; } getOrCreateImage(url, x, y, z) { let image = this.getImage(x, y, z); if (!image) { image = new TileImage(url, x, y, z); } return image; } /** * * @param {TileImage} imag */ loadImage(image) { // The queue already contains this image if (this.images.includes(image)) { return; } this.images.push(image); if (!this._rafLoop) { this.refresh(); } } refresh() { this._imagesDecodingCount = 0; this._imagesLoadingCount = 0; this.images = this.images.filter((image) => { if (image.loadState === LOAD_STATE.ERROR) { // remove if loaded with an error return false; } if (image.loadState === LOAD_STATE.LOADED && image.decodingState === DECODING_STATE.DECODED) { // remove if image is fully loaded and decoded return false; } if (!image.hasParent && image.decodingState === DECODING_STATE.IDLE) { // image not started decoding yet, and has no parent Tile that is attached, // we can safely remove it from queue return false; } if (image.loadState === LOAD_STATE.LOADING) { this._imagesLoadingCount++; } if (image.decodingState === DECODING_STATE.DECODING) { this._imagesDecodingCount++; return true; } return true; }); // Decode images that were loaded before, // but then were removed, and now are added back this.images.forEach((image) => { if (image.hasParent && image.decodingState === DECODING_STATE.IDLE && image.loadState === LOAD_STATE.LOADED) { // Instantly decode low-res images ignoring max count if (this._imagesDecodingCount < this.maxDecodingCount || image.isLowRes) { this._decodeImage(image); } } }); // Make sure we run requests simultaneously if (this._imagesLoadingCount < this.minBatchRequestCount) { // This should send network requests to load images this.images.forEach((image) => { if (image.hasParent && image.decodingState !== DECODING_STATE.DECODING && this._imagesDecodingCount < this.maxDecodingCount) { this._decodeImage(image); } }); } if (this.images.length === 0) { this.stop(); } else { this._loop(); } } _decodeImage(image) { this._imagesDecodingCount++; image.decode(); this.cacheImage(image); } _loop() { this._rafLoop = requestAnimationFrame(() => { this.refresh(); }); } stop() { if (this._rafLoop) { cancelAnimationFrame(this._rafLoop); this._rafLoop = null; } } /** * Add to queue * * @param {Array} tiles */ add(tiles) { const tilesThatWereLoadedBefore = []; const activeLayerTiles = []; const otherTiles = []; // todo: make tiles load from center? tiles.forEach((tile) => { if (tile.imageLoaded || tile.imageLoading) { tilesThatWereLoadedBefore.push(tile); } else if(tile.isInActiveLayer) { activeLayerTiles.push(tile); } else { otherTiles.push(tile); } }); tilesThatWereLoadedBefore.sort(function(tile1, tile2) { return tile1.z - tile2.z; }); this.images = this.images .concat(tilesThatWereLoadedBefore) .concat(activeLayerTiles) .concat(otherTiles); } clear() { this.images = []; this.stop(); } destroy() { this.clear(); this.cache.destroy(); this.cache = undefined; this.images = []; } } export default DecodingQueue;