UNPKG

create-dan

Version:

Dan frameworks

229 lines (192 loc) 5.31 kB
import { getAssetLoader, LoaderAssetStatus } from './loaders' import uniqid from 'uniqid' import { addRafOnce } from 'dan/raf' import Signal from 'signal' /** * Assets queue */ export default class AssetQueue { /** * Constructor * @param {Object} [options] - Options (cache, size) */ constructor({ cache = false, size = 0, autoStart = true, simultaneous = 0 } = {}) { this._assets = {} this.cache = cache this._autoStart = autoStart this._needsUpdate = false // Progress this._size = size this._autoSize = size < 1 // Queue this.simultaneous = simultaneous this._numberOfLoading = 0 // Events this.onProgress = new Signal() this.onComplete = new Signal() this.onError = new Signal() } /** * Alias for removeListener */ off() { this.removeListener.apply(this, arguments); } /** * Add a new asset to load * @param {*} header - Asset adress (URL, function, ...) * @param {Object} [options] - Asset options (cache, id, size) */ add(header, options = {}) { // Id options.id = typeof options.id === 'string' ?options.id :typeof header === 'string' ?header :uniqid() if(this._assets[options.id]) { throw new Error('Asset ID "'+options.id+'" is already in the queue') } // Cache options.cache = typeof options.cache === 'boolean' ?options.cache :this.cache // Get asset loader const loader = getAssetLoader(header, options) this._assets[options.id] = loader // Size if(this._autoSize) { this._size += loader.size } // Load const loading = loader.loading // Auto start if(this._autoStart) { this._update() } // Watch loading.then((body) => { this._update() }).catch((error) => { this.onError.dispatch(error) this._update() }) return loading } /** * Run the queue */ start() { this._autoStart = true if(this._numberOfLoading < this.simultaneous || this.simultaneous === 0) { let assets = [] for(let id in this._assets) { assets.push(this._assets[id]) } assets = assets.filter(asset => asset.status === LoaderAssetStatus.Waiting).sort((a, b) => a.priority - b.priority) const length = this.simultaneous === 0 ?assets.length :Math.min(this.simultaneous - this._numberOfLoading, assets.length) const next = () => { this._numberOfLoading-- if(this._autoStart) { this.start() } } for(let i = 0; i < length; i++) { this._numberOfLoading++ assets[i].load().then(next).catch(next) } } } /** * Stop the queue */ stop() { this._autoStart = false } /** * Control group status */ _update() { // Skip if queue was already waiting for update if(this._needsUpdate) { return } this._needsUpdate = true addRafOnce(() => { this._needsUpdate = false // AutoStart if(this._autoStart) { this.start() } // Percent const percent = this.progress // Progress this.onProgress.dispatch(percent) // Complete if(percent >= 1) { this.onComplete.dispatch(this.assets) } }) } /** * Remove an asset to the queue * @param {String} id - Asset id */ remove(id) { delete this._assets[id]; } /** * Total items size to load * @return {number} */ get progress() { let progress = 0, asset for(let id in this._assets) { asset = this._assets[id] // Complete or error if(asset.status === LoaderAssetStatus.Complete || asset.status === LoaderAssetStatus.Error) { progress += asset.size } } // Percent return progress / this._size } /** * Get an asset by id * @param {String} id - Asset id * @returns {*} - Asset body */ get(id) { const asset = this._assets[id] return asset?asset.body:null } /** * Get asset attributes * @param {*} id - Asset id * @return {Object} Attributes */ attributes(id) { const asset = this._assets[id] if(asset) { return asset.attributes } else { return null } } /** * Get all assets * @returns {Object} */ get assets() { const assets = {} for(let id in this._assets) { assets[id] = this._assets[id].body } return assets } } export {AssetQueue}