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">
237 lines (233 loc) • 8.06 kB
JavaScript
;
var warn = require('../../utils/logging/warn.js');
var path = require('../../utils/path.js');
var convertToList = require('../utils/convertToList.js');
var isSingleItem = require('../utils/isSingleItem.js');
;
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.warn(
`[Assets] "loadParser" is deprecated, use "parser" instead for ${url}`
);
}
if (!parser) {
warn.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.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.isSingleItem(assetsToLoadIn);
const assetsToLoad = convertToList.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.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.convertToList(assetsToUnloadIn, (item) => ({
alias: [item],
src: item
}));
const promises = assetsToUnload.map(async (asset) => {
const url = path.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.warn(`[Assets] parser should have an id`);
} else if (hash[parser.name] || hash[parser.id]) {
warn.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;
exports.Loader = Loader;
//# sourceMappingURL=Loader.js.map