// assets.js — 贴图 / 背景 / 特效帧的加载与缓存。 // // 默认走 PIXI.Assets.load;可由工厂注入 `loadTexture(url)` 覆盖(测试 / 自定义管线)。 // 容错(render-pixi §2 验收 10、§5.5):加载失败走 warn + 跳过,绝不抛断播放。 /** * 创建贴图加载器。 * @param {Object} [deps] * @param {*=} deps.PIXI Pixi 命名空间(含 Assets) * @param {((url:string)=>Promise<*>)=} deps.loadTexture 自定义加载器(默认 PIXI.Assets.load) * @returns {{ load:(url:string)=>Promise<*|null>, get:(url:string)=>(*|null) }} */ export function createAssetLoader({ PIXI, loadTexture } = {}) { /** @type {Map} 已就绪纹理缓存 */ const cache = new Map() /** @type {Map>} 进行中去重 */ const inflight = new Map() // 回退加载:用 HTMLImageElement(crossOrigin) 取图再建纹理。PIXI.Assets.load 的 fetch 路径对 // 某些跨域大图会 "Failed to fetch"(浏览器对大文件 fetch 的脆弱性/缓存污染),而 加载不受影响。 // 资源公共可读 + CORS 放行 → crossOrigin='anonymous' 的纹理不污染画布。 const loadViaImage = (/** @type {string} */ url) => new Promise((resolve, reject) => { const img = new Image() img.crossOrigin = 'anonymous' img.onload = () => { try { resolve(PIXI.Texture.from(img)) } catch (e) { reject(e) } } img.onerror = () => reject(new Error(`image load failed: ${url}`)) // 回退仅在主加载已失败时触发:用一个区分性 query 另起缓存条目,绕开可能被 no-cors 不透明响应 // 污染的原 URL 缓存(cors 请求命中不透明缓存会失败)。该 URL 自身仍可缓存。 img.src = url + (url.includes('?') ? '&' : '?') + '_pf=1' }) const loader = loadTexture || (/** @param {string} url */ (url) => { const hasAssets = PIXI && PIXI.Assets && typeof PIXI.Assets.load === 'function' const canImage = PIXI && PIXI.Texture && typeof PIXI.Texture.from === 'function' && typeof Image !== 'undefined' // 优先 Assets.load(带缓存/批处理);其 fetch 路径失败时回退 加载,保证大背景也能上屏。 if (hasAssets) { const p = PIXI.Assets.load(url) return canImage ? p.catch(() => loadViaImage(url)) : p } if (canImage) return loadViaImage(url) return Promise.reject(new Error('PIXI loader unavailable')) }) /** * 加载并缓存一张纹理。失败返回 null(warn),不抛。 * @param {string} url * @returns {Promise<*|null>} */ function load(url) { if (!url) return Promise.resolve(null) if (cache.has(url)) return Promise.resolve(cache.get(url)) if (inflight.has(url)) return /** @type {Promise<*|null>} */ (inflight.get(url)) const p = Promise.resolve() .then(() => loader(url)) .then((tex) => { cache.set(url, tex) inflight.delete(url) return tex }) .catch((e) => { inflight.delete(url) console.warn('[render-pixi] texture load fail', url, e && e.message) return null }) inflight.set(url, p) return p } /** 同步取已缓存纹理(未就绪返回 null)。 @param {string} url */ function get(url) { return cache.has(url) ? cache.get(url) : null } return { load, get } }