Make map editor preload texture previews
This commit is contained in:
+60
-15
@@ -9,6 +9,7 @@ const state = {
|
|||||||
imagePreviews: new Map(),
|
imagePreviews: new Map(),
|
||||||
imageLoading: new Set(),
|
imageLoading: new Set(),
|
||||||
imageFailures: new Set(),
|
imageFailures: new Set(),
|
||||||
|
previewLoadToken: 0,
|
||||||
history: [],
|
history: [],
|
||||||
dragging: null,
|
dragging: null,
|
||||||
};
|
};
|
||||||
@@ -143,26 +144,63 @@ function loadImage(url) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function preloadMapImages(map) {
|
async function preloadMapImages(map) {
|
||||||
for (const layer of map.layers) {
|
const textures = uniqueMapTextures(map);
|
||||||
for (const rect of layer.rects) {
|
if (!textures.length) return;
|
||||||
if (rect.texture) queueImagePreview(rect.texture);
|
|
||||||
|
const token = state.previewLoadToken + 1;
|
||||||
|
state.previewLoadToken = token;
|
||||||
|
setStatus(`正在加载贴图预览 0/${textures.length}`);
|
||||||
|
|
||||||
|
let loaded = 0;
|
||||||
|
let failed = 0;
|
||||||
|
await Promise.all(textures.map(async (texturePath) => {
|
||||||
|
const ok = await queueImagePreview(texturePath, { refreshAfterLoad: false });
|
||||||
|
if (ok) loaded += 1;
|
||||||
|
else failed += 1;
|
||||||
|
if (state.previewLoadToken === token) {
|
||||||
|
setStatus(`正在加载贴图预览 ${loaded + failed}/${textures.length}`);
|
||||||
}
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (state.previewLoadToken !== token) return;
|
||||||
|
refresh();
|
||||||
|
if (failed) {
|
||||||
|
setStatus(`贴图预览完成:成功 ${loaded},失败 ${failed}`);
|
||||||
|
} else {
|
||||||
|
setStatus(`贴图预览完成:${loaded}/${textures.length}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function queueImagePreview(texturePath) {
|
function uniqueMapTextures(map) {
|
||||||
|
const textures = [];
|
||||||
|
const seen = new Set();
|
||||||
|
for (const layer of map.layers) {
|
||||||
|
for (const rect of layer.rects) {
|
||||||
|
if (!rect.texture || seen.has(rect.texture)) continue;
|
||||||
|
seen.add(rect.texture);
|
||||||
|
textures.push(rect.texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return textures;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function queueImagePreview(texturePath, options = {}) {
|
||||||
if (!texturePath ||
|
if (!texturePath ||
|
||||||
state.imagePreviews.has(texturePath) ||
|
state.imagePreviews.has(texturePath)) {
|
||||||
state.imageLoading.has(texturePath) ||
|
return true;
|
||||||
state.imageFailures.has(texturePath)) {
|
}
|
||||||
return;
|
if (state.imageLoading.has(texturePath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (state.imageFailures.has(texturePath)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = previewUrlForTexture(texturePath);
|
const url = previewUrlForTexture(texturePath);
|
||||||
if (!url) {
|
if (!url) {
|
||||||
state.imageFailures.add(texturePath);
|
state.imageFailures.add(texturePath);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.imageLoading.add(texturePath);
|
state.imageLoading.add(texturePath);
|
||||||
@@ -170,10 +208,12 @@ async function queueImagePreview(texturePath) {
|
|||||||
const image = await loadImage(url);
|
const image = await loadImage(url);
|
||||||
state.imagePreviews.set(texturePath, image);
|
state.imagePreviews.set(texturePath, image);
|
||||||
state.imageFailures.delete(texturePath);
|
state.imageFailures.delete(texturePath);
|
||||||
refresh();
|
if (options.refreshAfterLoad !== false) refresh();
|
||||||
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
state.imageFailures.add(texturePath);
|
state.imageFailures.add(texturePath);
|
||||||
setStatus(`贴图预览加载失败:${texturePath}`);
|
setStatus(`贴图预览加载失败:${texturePath}`);
|
||||||
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
state.imageLoading.delete(texturePath);
|
state.imageLoading.delete(texturePath);
|
||||||
}
|
}
|
||||||
@@ -183,9 +223,14 @@ function previewUrlForTexture(texturePath) {
|
|||||||
const clean = String(texturePath || "").replace(/\\/g, "/").replace(/^\/+/, "");
|
const clean = String(texturePath || "").replace(/\\/g, "/").replace(/^\/+/, "");
|
||||||
if (!clean) return "";
|
if (!clean) return "";
|
||||||
if (/^(blob:|data:|https?:\/\/)/i.test(clean)) return clean;
|
if (/^(blob:|data:|https?:\/\/)/i.test(clean)) return clean;
|
||||||
if (clean.startsWith("assets/")) return `../../game/${clean}`;
|
if (clean.startsWith("assets/")) return cacheBustedUrl(`${location.origin}/game/${clean}`);
|
||||||
if (clean.startsWith("game/assets/")) return `../../${clean}`;
|
if (clean.startsWith("game/assets/")) return cacheBustedUrl(`${location.origin}/${clean}`);
|
||||||
return clean;
|
return cacheBustedUrl(clean);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cacheBustedUrl(url) {
|
||||||
|
const separator = url.includes("?") ? "&" : "?";
|
||||||
|
return `${url}${separator}preview=${Date.now()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadMapText(text, status) {
|
function loadMapText(text, status) {
|
||||||
@@ -194,10 +239,10 @@ function loadMapText(text, status) {
|
|||||||
state.selected = null;
|
state.selected = null;
|
||||||
state.history = [];
|
state.history = [];
|
||||||
state.imageFailures.clear();
|
state.imageFailures.clear();
|
||||||
preloadMapImages(state.map);
|
|
||||||
fitWorldToCanvas();
|
fitWorldToCanvas();
|
||||||
setStatus(status);
|
setStatus(status);
|
||||||
refresh();
|
refresh();
|
||||||
|
preloadMapImages(state.map);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setStatus(`解析失败:${error.message}`);
|
setStatus(`解析失败:${error.message}`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user