Initial commit: RHINE LAB · ANALYSIS OS:三维界面与动效实验(本仓库不含个人归档索引数据)
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<meta charset="utf-8" />
|
||||
<title>标题与查看器过渡检查</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background: #eae5e1;
|
||||
color: #252620;
|
||||
font: 13px monospace;
|
||||
}
|
||||
iframe {
|
||||
width: 1100px;
|
||||
height: 620px;
|
||||
border: 0;
|
||||
}
|
||||
pre {
|
||||
padding: 16px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
<iframe src="/?scene=archive" title="真实交互场景"></iframe>
|
||||
<pre id="result">正在检查…</pre>
|
||||
<script type="module">
|
||||
import { records } from "/src/data.ts";
|
||||
import { createRollingText } from "@kitlangton/rolling-number";
|
||||
import "@kitlangton/rolling-number/styles.css";
|
||||
const iframe = document.querySelector("iframe"),
|
||||
result = document.querySelector("#result");
|
||||
const checks = [];
|
||||
const pause = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
function check(ok, label) {
|
||||
if (!ok) throw new Error(label);
|
||||
checks.push(label);
|
||||
}
|
||||
async function until(fn) {
|
||||
const start = performance.now();
|
||||
while (!fn()) {
|
||||
if (performance.now() - start > 10000) throw new Error("等待状态超时");
|
||||
await pause(25);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await until(
|
||||
() =>
|
||||
iframe.contentWindow.rhine?.stats().loaded &&
|
||||
!iframe.contentDocument.querySelector("#loading") &&
|
||||
iframe.contentWindow.rhine.stats().mode === "archive",
|
||||
);
|
||||
const win = iframe.contentWindow,
|
||||
doc = iframe.contentDocument,
|
||||
app = win.rhine;
|
||||
app.archive();
|
||||
await pause(300);
|
||||
const title = doc.querySelector("#selected-title");
|
||||
const value = () => title.querySelector(".rn-value")?.textContent;
|
||||
const moving = () =>
|
||||
title
|
||||
.getAnimations({ subtree: true })
|
||||
.some((a) => a.playState === "running");
|
||||
app.select(1);
|
||||
await until(moving);
|
||||
check(
|
||||
value() === records[1].title,
|
||||
"普通切换播放文字滚动并立即更新可访问标题",
|
||||
);
|
||||
app.select(2);
|
||||
app.select(3);
|
||||
check(value() === records[3].title, "快速切换以最后选择为目标");
|
||||
check(
|
||||
win.getComputedStyle(title, "::after").content === "none",
|
||||
"快切期间不再显示黑色横条",
|
||||
);
|
||||
app.select(4);
|
||||
app.select(5);
|
||||
await pause(60);
|
||||
await until(() => !moving());
|
||||
check(value() === records[5].title, "停止后滚动落到最终标题");
|
||||
const finalGlyphs = [...title.querySelectorAll(".rn-visual .rn-face")]
|
||||
.map((face) => face.textContent)
|
||||
.join("");
|
||||
check(finalGlyphs === records[5].title, "滚动完成后没有残留的旧文字");
|
||||
app.select(6);
|
||||
app.select(7);
|
||||
app.detail();
|
||||
check(
|
||||
!moving() && value() === records[7].title,
|
||||
"切进详情结束标题滚动并保留最终文字",
|
||||
);
|
||||
await until(() => app.stats().canInspect);
|
||||
const opener = doc.querySelector('[data-action="model-viewer"]');
|
||||
opener.focus();
|
||||
opener.click();
|
||||
const viewer = doc.querySelector(".model-viewer");
|
||||
check(
|
||||
viewer.dataset.transition === "opening" && !viewer.hidden,
|
||||
"查看器保留进入动画阶段",
|
||||
);
|
||||
const before = win.getComputedStyle(viewer).opacity;
|
||||
viewer
|
||||
.querySelector("canvas")
|
||||
.dispatchEvent(
|
||||
new win.KeyboardEvent("keydown", { key: "Escape", bubbles: true }),
|
||||
);
|
||||
check(
|
||||
viewer.dataset.transition === "closing" && !viewer.hidden,
|
||||
"进入途中 Esc 转入退出动画",
|
||||
);
|
||||
check(
|
||||
doc.querySelector("#detail-ui").inert,
|
||||
"退出动画结束前详情仍不接收输入",
|
||||
);
|
||||
await until(() => viewer.hidden);
|
||||
check(
|
||||
!doc.querySelector("#detail-ui").inert && doc.activeElement === opener,
|
||||
"退出结束恢复详情与原按钮焦点",
|
||||
);
|
||||
opener.click();
|
||||
await until(
|
||||
() =>
|
||||
viewer.dataset.transition === "open" &&
|
||||
JSON.parse(viewer.dataset.stats || "{}").ready,
|
||||
);
|
||||
check(!viewer.hidden, "中途关闭后可重新打开并加载模型");
|
||||
viewer.querySelector('[data-viewer="explode"]').click();
|
||||
await pause(100);
|
||||
viewer.querySelector('[data-viewer="close"]').click();
|
||||
viewer.querySelector('[data-viewer="close"]').click();
|
||||
await until(() => viewer.hidden);
|
||||
check(
|
||||
!doc.querySelector("#detail-ui").inert,
|
||||
"拆解中重复关闭也能完整退出",
|
||||
);
|
||||
const host = document.createElement("span");
|
||||
host.style.cssText =
|
||||
"display:inline-block;font:24px system-ui;margin:16px";
|
||||
document.body.append(host);
|
||||
const text = createRollingText(host, {
|
||||
text: "初始",
|
||||
transition: "direct",
|
||||
stagger: "none",
|
||||
duration: 460,
|
||||
});
|
||||
await pause(100);
|
||||
text.update({ text: "长期实验研究记录" });
|
||||
await pause(90);
|
||||
text.update({ text: "光" });
|
||||
await pause(60);
|
||||
text.update({ text: "光学研究 🧬 / A-01" });
|
||||
await pause(100);
|
||||
await until(
|
||||
() =>
|
||||
!host
|
||||
.getAnimations({ subtree: true })
|
||||
.some((a) => a.playState === "running"),
|
||||
);
|
||||
check(
|
||||
host.querySelector(".rn-value").textContent === "光学研究 🧬 / A-01",
|
||||
"长短标题与中文、符号混排能正确收敛",
|
||||
);
|
||||
check(
|
||||
host.querySelectorAll(".rn-slot").length <=
|
||||
[...new Intl.Segmenter().segment("光学研究 🧬 / A-01")].length,
|
||||
"连续替换后回收旧字槽",
|
||||
);
|
||||
text.update({ text: "最终标题", animated: false });
|
||||
check(
|
||||
host.querySelector(".rn-value").textContent === "最终标题" &&
|
||||
host.getAnimations({ subtree: true }).length === 0,
|
||||
"减少动态效果立即显示最终标题",
|
||||
);
|
||||
text.destroy();
|
||||
host.remove();
|
||||
result.textContent = JSON.stringify({ passed: true, checks }, null, 2);
|
||||
} catch (error) {
|
||||
result.textContent = JSON.stringify(
|
||||
{ passed: false, checks, error: error.message },
|
||||
null,
|
||||
2,
|
||||
);
|
||||
console.error(error);
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
Reference in New Issue
Block a user