assets.js
3.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// 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<string, *>} 已就绪纹理缓存 */
const cache = new Map()
/** @type {Map<string, Promise<*|null>>} 进行中去重 */
const inflight = new Map()
// 回退加载:用 HTMLImageElement(crossOrigin) 取图再建纹理。PIXI.Assets.load 的 fetch 路径对
// 某些跨域大图会 "Failed to fetch"(浏览器对大文件 fetch 的脆弱性/缓存污染),而 <img> 加载不受影响。
// 资源公共可读 + 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 路径失败时回退 <img> 加载,保证大背景也能上屏。
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 }
}