UNPKG

pixi.js

Version:

<p align="center"> <a href="https://pixijs.com" target="_blank" rel="noopener noreferrer"> <img height="150" src="https://files.pixijs.download/branding/pixijs-logo-transparent-dark.svg?v=1" alt="PixiJS logo"> </a> </p> <br/> <p align="center">

235 lines (232 loc) 7.97 kB
import { warn } from '../../utils/logging/warn.mjs'; import { path } from '../../utils/path.mjs'; import { convertToList } from '../utils/convertToList.mjs'; import { isSingleItem } from '../utils/isSingleItem.mjs'; "use strict"; const _Loader = class _Loader { constructor() { /** * Options for loading assets with the loader. * These options will be used as defaults for all load calls made with this loader instance. * They can be overridden by passing options directly to the load method. * @example * ```ts * // Create a loader with custom default options * const loader = new Loader(); * loader.loadOptions = { * strategy: 'skip', // Default strategy to 'skip' * retryCount: 5, // Default retry count to 5 * retryDelay: 500, // Default retry delay to 500ms * }; * * // This load call will use the loader's default options * await loader.load('image1.png'); */ this.loadOptions = { ..._Loader.defaultOptions }; this._parsers = []; this._parsersValidated = false; /** * All loader parsers registered * @type {assets.LoaderParser[]} */ this.parsers = new Proxy(this._parsers, { set: (target, key, value) => { this._parsersValidated = false; target[key] = value; return true; } }); /** Cache loading promises that ae currently active */ this.promiseCache = {}; } /** function used for testing */ reset() { this._parsersValidated = false; this.promiseCache = {}; } /** * Used internally to generate a promise for the asset to be loaded. * @param url - The URL to be loaded * @param data - any custom additional information relevant to the asset being loaded * @returns - a promise that will resolve to an Asset for example a Texture of a JSON object */ _getLoadPromiseAndParser(url, data) { const result = { promise: null, parser: null }; result.promise = (async () => { let asset = null; let parser = null; if (data.parser || data.loadParser) { parser = this._parserHash[data.parser || data.loadParser]; if (data.loadParser) { warn( `[Assets] "loadParser" is deprecated, use "parser" instead for ${url}` ); } if (!parser) { warn( `[Assets] specified load parser "${data.parser || data.loadParser}" not found while loading ${url}` ); } } if (!parser) { for (let i = 0; i < this.parsers.length; i++) { const parserX = this.parsers[i]; if (parserX.load && parserX.test?.(url, data, this)) { parser = parserX; break; } } if (!parser) { warn(`[Assets] ${url} could not be loaded as we don't know how to parse it, ensure the correct parser has been added`); return null; } } asset = await parser.load(url, data, this); result.parser = parser; for (let i = 0; i < this.parsers.length; i++) { const parser2 = this.parsers[i]; if (parser2.parse) { if (parser2.parse && await parser2.testParse?.(asset, data, this)) { asset = await parser2.parse(asset, data, this) || asset; result.parser = parser2; } } } return asset; })(); return result; } async load(assetsToLoadIn, onProgressOrOptions) { if (!this._parsersValidated) { this._validateParsers(); } const options = typeof onProgressOrOptions === "function" ? { ..._Loader.defaultOptions, ...this.loadOptions, onProgress: onProgressOrOptions } : { ..._Loader.defaultOptions, ...this.loadOptions, ...onProgressOrOptions || {} }; const { onProgress, onError, strategy, retryCount, retryDelay } = options; let count = 0; const assets = {}; const singleAsset = isSingleItem(assetsToLoadIn); const assetsToLoad = convertToList(assetsToLoadIn, (item) => ({ alias: [item], src: item, data: {} })); const total = assetsToLoad.reduce((sum, asset) => sum + (asset.progressSize || 1), 0); const promises = assetsToLoad.map(async (asset) => { const url = path.toAbsolute(asset.src); if (assets[asset.src]) return; await this._loadAssetWithRetry(url, asset, { onProgress, onError, strategy, retryCount, retryDelay }, assets); count += asset.progressSize || 1; if (onProgress) onProgress(count / total); }); await Promise.all(promises); return singleAsset ? assets[assetsToLoad[0].src] : assets; } /** * Unloads one or more assets. Any unloaded assets will be destroyed, freeing up memory for your app. * The parser that created the asset, will be the one that unloads it. * @example * // Single asset: * const asset = await Loader.load('cool.png'); * * await Loader.unload('cool.png'); * * console.log(asset.destroyed); // true * @param assetsToUnloadIn - urls that you want to unload, or a single one! */ async unload(assetsToUnloadIn) { const assetsToUnload = convertToList(assetsToUnloadIn, (item) => ({ alias: [item], src: item })); const promises = assetsToUnload.map(async (asset) => { const url = path.toAbsolute(asset.src); const loadPromise = this.promiseCache[url]; if (loadPromise) { const loadedAsset = await loadPromise.promise; delete this.promiseCache[url]; await loadPromise.parser?.unload?.(loadedAsset, asset, this); } }); await Promise.all(promises); } /** validates our parsers, right now it only checks for name conflicts but we can add more here as required! */ _validateParsers() { this._parsersValidated = true; this._parserHash = this._parsers.filter((parser) => parser.name || parser.id).reduce((hash, parser) => { if (!parser.name && !parser.id) { warn(`[Assets] parser should have an id`); } else if (hash[parser.name] || hash[parser.id]) { warn(`[Assets] parser id conflict "${parser.id}"`); } hash[parser.name] = parser; if (parser.id) hash[parser.id] = parser; return hash; }, {}); } async _loadAssetWithRetry(url, asset, options, assets) { let attempt = 0; const { onError, strategy, retryCount, retryDelay } = options; const wait = (ms) => new Promise((r) => setTimeout(r, ms)); while (true) { try { if (!this.promiseCache[url]) { this.promiseCache[url] = this._getLoadPromiseAndParser(url, asset); } assets[asset.src] = await this.promiseCache[url].promise; return; } catch (e) { delete this.promiseCache[url]; delete assets[asset.src]; attempt++; const isLast = strategy !== "retry" || attempt > retryCount; if (strategy === "retry" && !isLast) { if (onError) onError(e, asset); await wait(retryDelay); continue; } if (strategy === "skip") { if (onError) onError(e, asset); return; } if (onError) onError(e, asset); const error = new Error(`[Loader.load] Failed to load ${url}. ${e}`); if (e instanceof Error && e.stack) { error.stack = e.stack; } throw error; } } } }; /** * Default options for loading assets * @example * ```ts * // Change default load options globally * Loader.defaultOptions = { * strategy: 'skip', // Change default strategy to 'skip' * retryCount: 5, // Change default retry count to 5 * retryDelay: 500, // Change default retry delay to 500ms * }; * ``` */ _Loader.defaultOptions = { onProgress: void 0, onError: void 0, strategy: "throw", retryCount: 3, retryDelay: 250 }; let Loader = _Loader; export { Loader }; //# sourceMappingURL=Loader.mjs.map