feat(all): 离线包加载测试
This commit is contained in:
@@ -0,0 +1,369 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>TkAstral3D 分包离线测试</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0b1020;
|
||||
--panel: rgba(15, 23, 42, 0.88);
|
||||
--line: rgba(148, 163, 184, 0.24);
|
||||
--text: #e2e8f0;
|
||||
--muted: #94a3b8;
|
||||
--accent: #22c55e;
|
||||
--accent-2: #38bdf8;
|
||||
--danger: #f87171;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(56, 189, 248, 0.12), transparent 32%),
|
||||
radial-gradient(circle at top right, rgba(34, 197, 94, 0.14), transparent 28%),
|
||||
linear-gradient(180deg, #0b1020, #050814 72%);
|
||||
color: var(--text);
|
||||
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
.app {
|
||||
display: grid;
|
||||
grid-template-columns: 360px 1fr;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding: 20px;
|
||||
border-right: 1px solid var(--line);
|
||||
background: var(--panel);
|
||||
backdrop-filter: blur(10px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.viewer-wrap {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#viewer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 24px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.desc,
|
||||
.hint,
|
||||
.meta,
|
||||
.log {
|
||||
color: var(--muted);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin-bottom: 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-top: 16px;
|
||||
padding: 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 14px;
|
||||
background: rgba(2, 6, 23, 0.36);
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 15px;
|
||||
color: #f8fafc;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
button,
|
||||
input[type="file"] {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(180deg, rgba(56, 189, 248, 0.16), rgba(34, 197, 94, 0.16));
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
width: 100%;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-top: 12px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--line);
|
||||
background: rgba(15, 23, 42, 0.72);
|
||||
font-size: 13px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.status.success {
|
||||
border-color: rgba(34, 197, 94, 0.35);
|
||||
color: #bbf7d0;
|
||||
}
|
||||
|
||||
.status.error {
|
||||
border-color: rgba(248, 113, 113, 0.35);
|
||||
color: #fecaca;
|
||||
}
|
||||
|
||||
.file-list {
|
||||
margin: 10px 0 0;
|
||||
padding-left: 18px;
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(56, 189, 248, 0.1);
|
||||
border: 1px solid rgba(56, 189, 248, 0.18);
|
||||
font-size: 12px;
|
||||
color: #bae6fd;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.app {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<aside class="sidebar">
|
||||
<h1>Tk 分包离线测试</h1>
|
||||
<div class="desc">
|
||||
这个页面直接使用 Tk 构建产物来验证离线包加载。
|
||||
仅支持分包模式:请一次选择多个 .astral 文件并加载。
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span class="tag">SDK: TkAstral3D/Test/dist/astral3d.umd.js</span>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>选择离线包</h2>
|
||||
<input id="fileInput" type="file" multiple accept=".astral" />
|
||||
<div class="row">
|
||||
<button id="loadBtn">加载离线包</button>
|
||||
<button id="loadLocalBtn" class="secondary">直接加载本目录</button>
|
||||
<button id="resetBtn" class="secondary">重置场景</button>
|
||||
</div>
|
||||
<div id="status" class="status">等待选择 .astral 文件,或点击“直接加载本目录”</div>
|
||||
<ul id="fileList" class="file-list"></ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>说明(分包)</h2>
|
||||
<div class="hint">
|
||||
1. 请选择导出的全部分包 .astral 文件。\n
|
||||
2. 至少包含入口包和依赖包后再点击加载。\n
|
||||
3. 也可点击“直接加载本目录”,默认读取 1121212.astral + 7ff6dd32-25b3-4eea-abdc-6f316ecd668e.astral。\n
|
||||
4. 建议通过本地静态服务器打开此页面,而不是直接 file:// 打开。
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="viewer-wrap">
|
||||
<div id="viewer"></div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="./dist/astral3d.umd.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const SDK = globalThis.Astral3D;
|
||||
if (!SDK) {
|
||||
document.getElementById("status").textContent = "Tk SDK 加载失败,请检查 astral3d.umd.js 路径";
|
||||
document.getElementById("status").className = "status error";
|
||||
return;
|
||||
}
|
||||
|
||||
const viewerEl = document.getElementById("viewer");
|
||||
const fileInput = document.getElementById("fileInput");
|
||||
const loadBtn = document.getElementById("loadBtn");
|
||||
const loadLocalBtn = document.getElementById("loadLocalBtn");
|
||||
const resetBtn = document.getElementById("resetBtn");
|
||||
const statusEl = document.getElementById("status");
|
||||
const fileListEl = document.getElementById("fileList");
|
||||
const LOCAL_SPLIT_FILES = [
|
||||
"1121212.astral",
|
||||
"7ff6dd32-25b3-4eea-abdc-6f316ecd668e.astral",
|
||||
];
|
||||
|
||||
let viewer = null;
|
||||
|
||||
function setStatus(text, type) {
|
||||
statusEl.textContent = text;
|
||||
statusEl.className = "status" + (type ? ` ${type}` : "");
|
||||
}
|
||||
|
||||
function guessEntryFile(files) {
|
||||
const uuidReg = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
||||
const findBaseName = name => name.replace(/\.[^.]+$/, "");
|
||||
return files.find(file => !uuidReg.test(findBaseName(file.name))) || files[0];
|
||||
}
|
||||
|
||||
function ensureViewer() {
|
||||
if (viewer) return viewer;
|
||||
viewer = new SDK.Viewer({
|
||||
container: viewerEl,
|
||||
edit: { enabled: false },
|
||||
grid: { enabled: true },
|
||||
request: { baseUrl: "" },
|
||||
});
|
||||
globalThis.viewer = viewer;
|
||||
return viewer;
|
||||
}
|
||||
|
||||
function resetViewer() {
|
||||
if (viewer && typeof viewer.dispose === "function") {
|
||||
viewer.dispose();
|
||||
}
|
||||
viewer = null;
|
||||
viewerEl.innerHTML = "";
|
||||
setStatus("场景已重置", "success");
|
||||
}
|
||||
|
||||
function renderFileList(files) {
|
||||
fileListEl.innerHTML = "";
|
||||
Array.from(files || []).forEach(file => {
|
||||
const li = document.createElement("li");
|
||||
li.textContent = `${file.name} (${Math.round(file.size / 1024)} KB)`;
|
||||
fileListEl.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
async function loadByOfflineFiles(files, entryName) {
|
||||
setStatus(`开始加载离线包:${entryName}`, "");
|
||||
const currentViewer = ensureViewer();
|
||||
const packer = currentViewer.package || new SDK.Package(currentViewer);
|
||||
currentViewer.package = packer;
|
||||
|
||||
await packer.unpack({
|
||||
url: entryName,
|
||||
offlineFiles: files,
|
||||
offlineEntry: entryName,
|
||||
onProgress: progress => {
|
||||
setStatus(`离线包加载中... ${Number(progress || 0).toFixed(2)}%`, "");
|
||||
},
|
||||
onSceneLoad: () => {
|
||||
setStatus("场景首包已解析,正在继续加载资源...", "");
|
||||
},
|
||||
onComplete: () => {
|
||||
setStatus(`离线包加载完成:${entryName}`, "success");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchLocalAstralFile(name) {
|
||||
const response = await fetch(`./${encodeURIComponent(name)}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`读取本地文件失败:${name}`);
|
||||
}
|
||||
const blob = await response.blob();
|
||||
return new File([blob], name, { type: "application/octet-stream" });
|
||||
}
|
||||
|
||||
async function loadOfflinePackage() {
|
||||
const files = Array.from(fileInput.files || []).filter(f => f.name.toLowerCase().endsWith(".astral"));
|
||||
if (files.length < 2) {
|
||||
setStatus("分包模式请至少选择 2 个 .astral 文件", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
renderFileList(files);
|
||||
try {
|
||||
const entry = guessEntryFile(files);
|
||||
await loadByOfflineFiles(files, entry.name);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
setStatus(`加载失败:${error && error.message ? error.message : error}`, "error");
|
||||
}
|
||||
}
|
||||
|
||||
async function loadLocalPackages() {
|
||||
try {
|
||||
renderFileList(LOCAL_SPLIT_FILES.map(name => ({ name, size: 0 })));
|
||||
const files = [];
|
||||
for (const name of LOCAL_SPLIT_FILES) {
|
||||
files.push(await fetchLocalAstralFile(name));
|
||||
}
|
||||
await loadByOfflineFiles(files, LOCAL_SPLIT_FILES[0]);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
setStatus(`本目录加载失败:${error && error.message ? error.message : error}`, "error");
|
||||
}
|
||||
}
|
||||
|
||||
fileInput.addEventListener("change", () => {
|
||||
renderFileList(fileInput.files);
|
||||
setStatus("文件已选择,点击“加载离线包”开始测试", "");
|
||||
});
|
||||
|
||||
loadBtn.addEventListener("click", () => {
|
||||
loadOfflinePackage();
|
||||
});
|
||||
|
||||
loadLocalBtn.addEventListener("click", () => {
|
||||
loadLocalPackages();
|
||||
});
|
||||
|
||||
resetBtn.addEventListener("click", () => {
|
||||
resetViewer();
|
||||
});
|
||||
|
||||
ensureViewer();
|
||||
setStatus("Tk SDK 已加载(分包模式),等待选择离线包", "success");
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user