daaaa
This commit is contained in:
parent
8eb66f2e66
commit
33234f32ae
18 changed files with 1390 additions and 573 deletions
140
js/assets.js
140
js/assets.js
|
|
@ -2,10 +2,15 @@
|
|||
const images = new Map();
|
||||
const imageBoundsCache = new WeakMap();
|
||||
const imageMetrics = new Map();
|
||||
const imagePromises = new Map();
|
||||
const imageAssetIndex = new Map();
|
||||
|
||||
const scaledSpriteCache = new Map();
|
||||
const stainOverlayCache = new Map();
|
||||
|
||||
const INITIAL_IMAGE_IDS = new Set(["smile", "zunchi", "zunchi_02"]);
|
||||
const EARLY_IMAGE_IDS = new Set(["smile", "angry", "teary", "jito", "drool", "cry", "sleep", "pokan", "normal_smirk", "normal_tongue", "normal_happy", "weak", "hurt", "hurt2", "zunchi", "zunchi_02"]);
|
||||
|
||||
function trimCanvasCache(cache, limit = 420) {
|
||||
while (cache.size > limit) {
|
||||
const first = cache.keys().next().value;
|
||||
|
|
@ -79,32 +84,111 @@ function getCachedStainOverlayCanvas(id, img, w, h, stain = 0, seed = 0.5) {
|
|||
return canvas;
|
||||
}
|
||||
|
||||
function loadImages() {
|
||||
const assets = [...SPRITES, ...DECOR_ASSETS];
|
||||
let loaded = 0;
|
||||
return new Promise((resolve) => {
|
||||
for (const s of assets) {
|
||||
const img = new Image();
|
||||
img.onload = img.onerror = () => {
|
||||
const w = img.naturalWidth || img.width || ALPHA_BOUNDS[s.id]?.imageW || 512;
|
||||
const h = img.naturalHeight || img.height || ALPHA_BOUNDS[s.id]?.imageH || 512;
|
||||
const bounds = ALPHA_BOUNDS[s.id] || { x: 0, y: 0, w, h, imageW: w, imageH: h };
|
||||
imageMetrics.set(s.id, {
|
||||
w,
|
||||
h,
|
||||
ratio: h / Math.max(w, 1),
|
||||
visibleBottomRatio: (bounds.y + bounds.h) / Math.max(bounds.imageH, 1),
|
||||
});
|
||||
loaded += 1;
|
||||
if (loaded === assets.length) resolve();
|
||||
};
|
||||
img.alphaBounds = ALPHA_BOUNDS[s.id] || null;
|
||||
img.src = s.path;
|
||||
images.set(s.id, img);
|
||||
}
|
||||
function primeImageMetrics(asset) {
|
||||
if (!asset || imageMetrics.has(asset.id)) return;
|
||||
const bounds = ALPHA_BOUNDS[asset.id];
|
||||
const w = bounds?.imageW || 512;
|
||||
const h = bounds?.imageH || 512;
|
||||
imageMetrics.set(asset.id, {
|
||||
w,
|
||||
h,
|
||||
ratio: h / Math.max(w, 1),
|
||||
visibleBottomRatio: bounds ? (bounds.y + bounds.h) / Math.max(bounds.imageH, 1) : 1,
|
||||
});
|
||||
}
|
||||
|
||||
function setupAssetIndex() {
|
||||
if (imageAssetIndex.size) return;
|
||||
for (const asset of [...SPRITES, ...DECOR_ASSETS]) {
|
||||
imageAssetIndex.set(asset.id, asset);
|
||||
primeImageMetrics(asset);
|
||||
}
|
||||
}
|
||||
|
||||
function markImageLoaded(asset, img) {
|
||||
const w = img.naturalWidth || img.width || ALPHA_BOUNDS[asset.id]?.imageW || 512;
|
||||
const h = img.naturalHeight || img.height || ALPHA_BOUNDS[asset.id]?.imageH || 512;
|
||||
const bounds = ALPHA_BOUNDS[asset.id] || { x: 0, y: 0, w, h, imageW: w, imageH: h };
|
||||
imageMetrics.set(asset.id, {
|
||||
w,
|
||||
h,
|
||||
ratio: h / Math.max(w, 1),
|
||||
visibleBottomRatio: (bounds.y + bounds.h) / Math.max(bounds.imageH, 1),
|
||||
});
|
||||
}
|
||||
|
||||
function isImageReady(id) {
|
||||
const img = images.get(id);
|
||||
return !!(img && img.complete && (img.naturalWidth || img.width));
|
||||
}
|
||||
|
||||
function loadImageAsset(asset, priority = "auto") {
|
||||
if (!asset) return Promise.resolve(null);
|
||||
setupAssetIndex();
|
||||
primeImageMetrics(asset);
|
||||
if (isImageReady(asset.id)) return Promise.resolve(images.get(asset.id));
|
||||
if (imagePromises.has(asset.id)) return imagePromises.get(asset.id);
|
||||
const img = images.get(asset.id) || new Image();
|
||||
img.decoding = "async";
|
||||
if ("fetchPriority" in img) img.fetchPriority = priority;
|
||||
img.alphaBounds = ALPHA_BOUNDS[asset.id] || null;
|
||||
images.set(asset.id, img);
|
||||
const promise = new Promise((resolve) => {
|
||||
img.onload = () => { markImageLoaded(asset, img); resolve(img); };
|
||||
img.onerror = () => { resolve(null); };
|
||||
});
|
||||
imagePromises.set(asset.id, promise);
|
||||
if (!img.src) img.src = asset.path;
|
||||
return promise;
|
||||
}
|
||||
|
||||
function ensureImage(id) {
|
||||
setupAssetIndex();
|
||||
const asset = imageAssetIndex.get(id);
|
||||
if (!asset) return null;
|
||||
primeImageMetrics(asset);
|
||||
const existing = images.get(id);
|
||||
if (!existing || !existing.src) loadImageAsset(asset, "low");
|
||||
return images.get(id) || null;
|
||||
}
|
||||
|
||||
function getRenderableImage(id, fallbackId = "smile") {
|
||||
if (id) ensureImage(id);
|
||||
if (isImageReady(id)) return images.get(id);
|
||||
if (fallbackId && fallbackId !== id) ensureImage(fallbackId);
|
||||
if (isImageReady(fallbackId)) return images.get(fallbackId);
|
||||
return null;
|
||||
}
|
||||
|
||||
function loadImages(ids = null) {
|
||||
setupAssetIndex();
|
||||
const requested = ids ? new Set(ids) : INITIAL_IMAGE_IDS;
|
||||
const tasks = [];
|
||||
for (const id of requested) {
|
||||
const asset = imageAssetIndex.get(id);
|
||||
if (asset) tasks.push(loadImageAsset(asset, "high"));
|
||||
}
|
||||
return Promise.all(tasks).then(() => undefined);
|
||||
}
|
||||
|
||||
function startBackgroundImageLoading() {
|
||||
setupAssetIndex();
|
||||
const allAssets = [...SPRITES, ...DECOR_ASSETS];
|
||||
const loadGroup = (ids, priority = "low") => {
|
||||
for (const id of ids) {
|
||||
const asset = imageAssetIndex.get(id);
|
||||
if (asset) loadImageAsset(asset, priority);
|
||||
}
|
||||
};
|
||||
const schedule = window.requestIdleCallback
|
||||
? (fn, timeout = 1200) => window.requestIdleCallback(fn, { timeout })
|
||||
: (fn) => setTimeout(fn, 60);
|
||||
schedule(() => loadGroup(EARLY_IMAGE_IDS, "auto"), 450);
|
||||
schedule(() => {
|
||||
for (const asset of allAssets) loadImageAsset(asset, "low");
|
||||
}, 2200);
|
||||
}
|
||||
|
||||
function imageAlphaBounds(img) {
|
||||
if (!img) return null;
|
||||
if (img.alphaBounds) return img.alphaBounds;
|
||||
|
|
@ -119,7 +203,12 @@ function imageAlphaBounds(img) {
|
|||
}
|
||||
|
||||
function getImageMetrics(idOrImg) {
|
||||
if (typeof idOrImg === "string") return imageMetrics.get(idOrImg) || null;
|
||||
if (typeof idOrImg === "string") {
|
||||
setupAssetIndex();
|
||||
const asset = imageAssetIndex.get(idOrImg);
|
||||
if (asset) primeImageMetrics(asset);
|
||||
return imageMetrics.get(idOrImg) || null;
|
||||
}
|
||||
if (!idOrImg) return null;
|
||||
for (const [id, img] of images) {
|
||||
if (img === idOrImg) return imageMetrics.get(id) || null;
|
||||
|
|
@ -129,5 +218,6 @@ function getImageMetrics(idOrImg) {
|
|||
return w && h ? { w, h, ratio: h / w, visibleBottomRatio: 1 } : null;
|
||||
}
|
||||
|
||||
setupAssetIndex();
|
||||
|
||||
window.TarinaiAssets = { images, imageBoundsCache, imageMetrics, scaledSpriteCache, stainOverlayCache, loadImages, imageAlphaBounds, getImageMetrics, getCachedSpriteCanvas, getCachedStainOverlayCanvas };
|
||||
window.TarinaiAssets = { images, imageBoundsCache, imageMetrics, imagePromises, scaledSpriteCache, stainOverlayCache, loadImages, startBackgroundImageLoading, ensureImage, isImageReady, getRenderableImage, imageAlphaBounds, getImageMetrics, getCachedSpriteCanvas, getCachedStainOverlayCanvas };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue