| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <title>原型 · 低精度到高精度的交接</title>
- <style>
- :root { color-scheme: light; --ink: #1c2430; --muted: #5c6775; --line: #ddd4c6; --paper: #f6f3ec; --card: #fffdf8; --accent: #1f6f8b; --bad: #8c3a2f; --ok: #2d6a4f; }
- * { box-sizing: border-box; }
- body { margin: 0; font: 16px/1.5 "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; color: var(--ink); background: var(--paper); }
- main { max-width: 980px; margin: 0 auto; padding: 32px 20px 80px; }
- h1 { font-size: 28px; line-height: 1.25; margin: 0 0 8px; }
- h2 { font-size: 18px; margin: 28px 0 10px; }
- p { margin: 0 0 12px; }
- .eyebrow { color: var(--accent); font-weight: 700; letter-spacing: 0.04em; }
- .question, .assume, .panel, .walk { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 16px 18px; }
- .assume { border-color: #e2c48a; background: #fff8ea; }
- .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 10px; }
- .field { border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; background: #fff; }
- .field b { display: block; font-size: 13px; color: var(--muted); font-weight: 600; }
- .field.changed { border-color: var(--accent); box-shadow: inset 3px 0 0 var(--accent); }
- .groups { display: grid; gap: 14px; }
- .group { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
- .group span { min-width: 4.5em; color: var(--muted); font-size: 14px; }
- button { font: inherit; border: 1px solid #b9c7cf; background: #fff; color: var(--ink); border-radius: 999px; padding: 7px 12px; cursor: pointer; }
- button:hover { border-color: var(--accent); }
- button:disabled { opacity: 0.45; cursor: default; }
- button.primary { background: var(--accent); color: white; border-color: var(--accent); }
- .tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
- .tabs button[aria-selected="true"] { background: var(--ink); color: white; border-color: var(--ink); }
- .steps { display: grid; gap: 8px; margin-top: 12px; }
- .note { color: var(--muted); }
- .bad { color: var(--bad); }
- .ok { color: var(--ok); }
- code { font-family: ui-monospace, Consolas, monospace; font-size: 0.92em; }
- </style>
- </head>
- <body>
- <main>
- <p class="eyebrow">可丢弃原型 · 双击即可打开 · 不写入正式地图</p>
- <h1>低精度到高精度,边缘什么时候可以出现</h1>
- <p>用按钮把「全球 → 进入县城 → 锁定 → 返回 / 取消」走一遍,看精细影像会不会早于遮挡出现。</p>
- <section class="question">
- <p><strong>要回答的问题:</strong>在已确认的「先藏精细影像、雾先挡住、再渐显;返回时反过来」下,取消、雾失败、瓦片早晚和过期操作,会不会让矩形边缘先露出来。</p>
- <p class="note">这是逻辑原型,不是地球画面。它不证明肉眼看不到方块,也不证明帧耗时。保护是否成立由你显式标记,不能从「雾强度已经拉满」推出来。</p>
- </section>
- <h2>还没经你确认的假设</h2>
- <section class="assume">
- <p>下面这些只为让按钮能跑,点出来不对就改,不要当成验收通过:</p>
- <p>渐显和收起各分四下,只是按钮粒度,不是时长。亮起前除了保护成立,还要等「目标视野可显示」;瓦片失败时保持隐藏、继续用粗底图。返回收到看不见之后才回到全球。矩形内部换级不会被整层透明度修掉。</p>
- </section>
- <h2>当前状态</h2>
- <section id="state" class="panel"></section>
- <p id="delta" class="note"></p>
- <p id="refusal"></p>
- <h2>自由试</h2>
- <div id="free" class="groups"></div>
- <h2>对照走查</h2>
- <section class="walk">
- <div id="tabs" class="tabs"></div>
- <p id="blurb"></p>
- <div id="steps" class="steps"></div>
- </section>
- </main>
- <script>
- const Transition = (() => {
- const STEP = 0.25
- function createState() {
- return {
- phase: "global",
- intent: "idle",
- generation: 0,
- fogSupported: true,
- fogReady: false,
- fogStrength: 0,
- fogFailure: null,
- protection: false,
- alpha: 0,
- reveal: "hidden",
- prefetch: "idle",
- promise: "none",
- edgeExposed: false,
- internalSwap: false,
- notice: "全球和引导阶段只看粗底图,精细影像藏着。",
- refusal: null
- }
- }
- function next(state, patch) {
- return Object.assign({}, state, patch, { refusal: null })
- }
- function refuse(state, reason) {
- return Object.assign({}, state, { refusal: reason })
- }
- function fogExpected(state) {
- return state.fogSupported && !state.fogFailure
- }
- function beginPrefetch(state) {
- if (state.prefetch === "ready" || state.prefetch === "fetching") return state.prefetch
- return "fetching"
- }
- function syncReveal(state) {
- if (state.intent === "hide") {
- return state.alpha > 0 ? "concealing" : "hidden"
- }
- if (state.intent !== "show") return state.alpha > 0 ? "shown" : "hidden"
- if (state.alpha >= 1) return "shown"
- if (state.alpha > 0) return "revealing"
- return "hidden"
- }
- function promiseFor(state) {
- if (state.alpha <= 0) return "none"
- if (state.protection) return "covered"
- return "degraded"
- }
- function enter(state, phase) {
- const showing = state.alpha > 0
- return next(state, {
- phase: phase,
- intent: "show",
- generation: state.generation + 1,
- prefetch: beginPrefetch(state),
- reveal: syncReveal({ intent: "show", alpha: state.alpha }),
- promise: promiseFor(state),
- notice: showing
- ? "最新目标改成进入。过期的收起作废,精细层停在当前透明度。"
- : "已进入交接。精细层仍不可见,并开始预取瓦片。"
- })
- }
- function leave(state, label) {
- return next(state, {
- phase: "returning",
- intent: "hide",
- generation: state.generation + 1,
- reveal: state.alpha > 0 ? "concealing" : "hidden",
- notice: state.alpha > 0
- ? label + "立刻开始收起精细层,不把正在变亮的过程放完。"
- : label + "时精细层本来就看不见。"
- })
- }
- function reduce(state, action) {
- switch (action.type) {
- case "prepareFog":
- if (!state.fogSupported || state.fogFailure) return refuse(state, "雾当前不可用,准备不会变成保护。")
- return next(state, { fogReady: true, notice: "雾处理级可执行。强度仍是 0,边缘还没被挡住。" })
- case "raiseFog":
- if (!state.fogReady || !fogExpected(state)) return refuse(state, "雾还不能执行,不能把强度拉满。")
- return next(state, { fogStrength: 1, notice: "雾强度已满。这仍不等于矩形边缘已经被挡住。" })
- case "markProtection":
- if (!(state.fogReady && state.fogStrength >= 1 && fogExpected(state))) {
- return refuse(state, "保护不能由准备完成或强度单独成立。")
- }
- return next(state, { protection: true, promise: state.alpha > 0 ? "covered" : state.promise, notice: "已标记保护成立。精细层不会因此自动亮起。" })
- case "loseProtection":
- return next(state, {
- protection: false,
- promise: state.alpha > 0 ? "degraded" : "none",
- edgeExposed: state.edgeExposed || (state.alpha > 0 && fogExpected(state)),
- notice: state.alpha > 0 ? "保护消失时精细层还看得到。" : "保护已取消,精细层当时看不见。"
- })
- case "fogFailed":
- return next(state, {
- fogSupported: false,
- fogReady: false,
- fogStrength: 0,
- fogFailure: "failed",
- protection: false,
- promise: state.alpha > 0 ? "degraded" : "none",
- edgeExposed: state.edgeExposed || state.alpha > 0,
- notice: state.alpha > 0
- ? "雾已关闭并提示。精细层不突然藏起来,但不再承诺挡住边缘。"
- : "雾已关闭并提示。精细层当时看不见,业务不无限等待。"
- })
- case "fogRecovered":
- return next(state, {
- fogSupported: true,
- fogFailure: null,
- notice: "雾恢复为可准备。不会自动标成已经挡住边缘。"
- })
- case "enter":
- if (state.phase === "locked" && state.intent === "show") return refuse(state, "已经锁定在主场景,不用再次进入。")
- return enter(state, "entering")
- case "arrive":
- if (state.intent !== "show") return refuse(state, "当前目标不是进入,不能锁定主场景。")
- return next(state, { phase: "locked", notice: "相机已到主场景。精细层是否可见仍看保护和瓦片,不看锁定本身。" })
- case "cancel":
- if (state.phase !== "entering") return refuse(state, "只有正在进入时可以取消。已经锁定请用返回全球。")
- return leave(state, "取消进入")
- case "returnGlobal":
- if (state.phase === "global" && state.alpha === 0) return refuse(state, "已经在全球,精细层也看不见。")
- return leave(state, "返回全球")
- case "tilesReady":
- if (state.prefetch === "failed" && state.intent !== "show") return refuse(state, "这次加载已失败。要重试请先再次进入。")
- return next(state, { prefetch: "ready", notice: "目标视野现在可显示。是否亮起仍要看当前目标和保护。" })
- case "tilesFailed":
- if (state.alpha > 0) {
- return next(state, { prefetch: "failed", internalSwap: true, notice: "新的一级没到,已经显示的精细层保持原样。整层透明度修不了内部换挡。" })
- }
- return next(state, { prefetch: "failed", reveal: "hidden", notice: "瓦片失败。精细层继续隐藏,画面留在粗底图。" })
- case "revealStep": {
- if (state.intent !== "show") return refuse(state, "当前目标不是亮起精细层,过期的变亮无效。")
- if (state.prefetch !== "ready") return refuse(state, "目标视野还不能显示,先不亮,避免空白后突然跳出方块。")
- if (!state.protection && fogExpected(state)) return refuse(state, "雾仍被指望遮住边缘,但保护还没成立,不能亮起精细层。")
- if (state.alpha >= 1) return refuse(state, "精细层已经完全可见。")
- const alpha = Math.min(1, Math.round((state.alpha + STEP) * 100) / 100)
- const exposed = state.edgeExposed || !state.protection
- return next(state, {
- alpha: alpha,
- reveal: alpha >= 1 ? "shown" : "revealing",
- promise: state.protection ? "covered" : "degraded",
- edgeExposed: exposed,
- notice: state.protection ? "在保护成立后渐显一档。" : "无雾降级:精细层仍渐显,但不承诺挡住矩形边。"
- })
- }
- case "concealStep": {
- if (state.intent !== "hide") return refuse(state, "当前目标不是收起,过期的变暗无效。")
- if (state.alpha <= 0) return refuse(state, "精细层已经看不见。")
- const alpha = Math.max(0, Math.round((state.alpha - STEP) * 100) / 100)
- const done = alpha === 0
- return next(state, {
- alpha: alpha,
- reveal: done ? "hidden" : "concealing",
- promise: done ? "none" : promiseFor(Object.assign({}, state, { alpha: alpha })),
- phase: done ? "global" : "returning",
- intent: done ? "idle" : "hide",
- notice: done ? "精细层已收起,可以再撤雾。现在回到全球。" : "精细层再收起一档。雾先留着。"
- })
- }
- case "dropFog":
- if (state.alpha > 0) {
- return next(state, {
- fogStrength: 0,
- protection: false,
- promise: "degraded",
- edgeExposed: true,
- notice: "精细层还在时雾被撤掉,矩形边缘可能露出。这不是合格的返回顺序。"
- })
- }
- return next(state, { fogStrength: 0, protection: false, fogReady: state.fogReady, notice: "精细层已经看不见,撤雾不会露出矩形边缘。" })
- case "levelSwap":
- return next(state, {
- internalSwap: true,
- notice: state.alpha > 0 ? "矩形内部换了一级。整层透明度不能只淡新到的那一级。" : "换级发生时精细层不可见,画面上还没有跳变。"
- })
- default:
- return refuse(state, "没有这个操作。")
- }
- }
- return { createState: createState, reduce: reduce }
- })()
- const PHASE = { global: "全球", entering: "正在进入", locked: "已锁定主场景", returning: "正在返回" }
- const REVEAL = { hidden: "隐藏", revealing: "正在亮起", shown: "已显示", concealing: "正在收起" }
- const PREFETCH = { idle: "未请求", fetching: "预取中,仍不可见", ready: "目标视野可显示", failed: "加载失败" }
- const PROMISE = { none: "无", covered: "有保护", degraded: "已降级,不承诺遮边" }
- const FIELDS = [
- ["phase", "阶段", (s) => PHASE[s.phase]],
- ["intent", "当前目标", (s) => ({ idle: "闲置", show: "亮起精细层", hide: "收起精细层" }[s.intent])],
- ["generation", "只认最新操作", (s) => "第 " + s.generation + " 次"],
- ["fogSupported", "雾是否可用", (s) => s.fogSupported ? "可用" : "已关闭"],
- ["fogReady", "雾是否可执行", (s) => s.fogReady ? "可执行" : "还不能执行"],
- ["fogStrength", "雾强度", (s) => Math.round(s.fogStrength * 100) + "%"],
- ["protection", "保护是否成立", (s) => s.protection ? "成立" : "未成立"],
- ["alpha", "精细层透明度", (s) => Math.round(s.alpha * 100) + "%"],
- ["reveal", "精细层显示", (s) => REVEAL[s.reveal]],
- ["prefetch", "瓦片", (s) => PREFETCH[s.prefetch]],
- ["promise", "遮缝承诺", (s) => PROMISE[s.promise]],
- ["edgeExposed", "边缘是否可能已露出", (s) => s.edgeExposed ? "是" : "否"],
- ["internalSwap", "内部换级", (s) => s.internalSwap ? "发生过,整层透明度治不了" : "还没有"]
- ]
- const FREE = [
- ["雾", [
- ["准备雾", { type: "prepareFog" }],
- ["把雾强度拉满", { type: "raiseFog" }],
- ["标记保护已成立", { type: "markProtection" }],
- ["标记保护消失", { type: "loseProtection" }],
- ["雾运行失败", { type: "fogFailed" }],
- ["雾恢复可准备", { type: "fogRecovered" }],
- ["撤掉雾", { type: "dropFog" }]
- ]],
- ["进出", [
- ["点击进入系统", { type: "enter" }],
- ["飞到并锁定", { type: "arrive" }],
- ["取消进入", { type: "cancel" }],
- ["返回全球", { type: "returnGlobal" }]
- ]],
- ["影像", [
- ["目标视野可显示", { type: "tilesReady" }],
- ["瓦片加载失败", { type: "tilesFailed" }],
- ["精细层再亮一档", { type: "revealStep" }],
- ["精细层再暗一档", { type: "concealStep" }],
- ["矩形内部换了一级", { type: "levelSwap" }]
- ]]
- ]
- const SCENARIOS = [
- { id: "forward", title: "正向进入", blurb: "看精细层会不会在保护成立前亮起。强度拉满之后仍应不能亮。", steps: [
- ["点击进入系统", { type: "enter" }], ["准备雾", { type: "prepareFog" }], ["把雾强度拉满", { type: "raiseFog" }],
- ["目标视野可显示", { type: "tilesReady" }], ["精细层再亮一档", { type: "revealStep" }], ["标记保护已成立", { type: "markProtection" }],
- ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }],
- ["精细层再亮一档", { type: "revealStep" }], ["飞到并锁定", { type: "arrive" }]
- ]},
- { id: "strength", title: "只有强度", blurb: "复现「雾看起来满了,但还没挡住边缘」。这一下变亮必须被拒绝。", steps: [
- ["点击进入系统", { type: "enter" }], ["准备雾", { type: "prepareFog" }], ["把雾强度拉满", { type: "raiseFog" }],
- ["目标视野可显示", { type: "tilesReady" }], ["精细层再亮一档", { type: "revealStep" }]
- ]},
- { id: "back", title: "返回先收影像", blurb: "先把精细层收到看不见,再撤雾。走完后边缘应仍是「否」。", steps: [
- ["点击进入系统", { type: "enter" }], ["准备雾", { type: "prepareFog" }], ["把雾强度拉满", { type: "raiseFog" }],
- ["标记保护已成立", { type: "markProtection" }], ["目标视野可显示", { type: "tilesReady" }],
- ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }],
- ["返回全球", { type: "returnGlobal" }],
- ["精细层再暗一档", { type: "concealStep" }], ["精细层再暗一档", { type: "concealStep" }], ["精细层再暗一档", { type: "concealStep" }], ["精细层再暗一档", { type: "concealStep" }],
- ["撤掉雾", { type: "dropFog" }]
- ]},
- { id: "earlyFog", title: "返回时先撤雾", blurb: "不合格顺序:精细层还在就撤雾。边缘应变成「是」。", steps: [
- ["点击进入系统", { type: "enter" }], ["准备雾", { type: "prepareFog" }], ["把雾强度拉满", { type: "raiseFog" }],
- ["标记保护已成立", { type: "markProtection" }], ["目标视野可显示", { type: "tilesReady" }],
- ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }],
- ["返回全球", { type: "returnGlobal" }], ["撤掉雾", { type: "dropFog" }]
- ]},
- { id: "cancel", title: "取消正在变亮", blurb: "亮到一半改取消。下一档变亮应失效,接着只应能收起。", steps: [
- ["点击进入系统", { type: "enter" }], ["准备雾", { type: "prepareFog" }], ["把雾强度拉满", { type: "raiseFog" }],
- ["标记保护已成立", { type: "markProtection" }], ["目标视野可显示", { type: "tilesReady" }],
- ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }],
- ["取消进入", { type: "cancel" }], ["精细层再亮一档", { type: "revealStep" }], ["精细层再暗一档", { type: "concealStep" }]
- ]},
- { id: "reenter", title: "返回途中再进入", blurb: "收起还没完成就再次进入。过期的变暗应失效,精细层停在当前透明度。", steps: [
- ["点击进入系统", { type: "enter" }], ["准备雾", { type: "prepareFog" }], ["把雾强度拉满", { type: "raiseFog" }],
- ["标记保护已成立", { type: "markProtection" }], ["目标视野可显示", { type: "tilesReady" }],
- ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }],
- ["返回全球", { type: "returnGlobal" }], ["精细层再暗一档", { type: "concealStep" }], ["精细层再暗一档", { type: "concealStep" }],
- ["点击进入系统", { type: "enter" }], ["精细层再暗一档", { type: "concealStep" }], ["精细层再亮一档", { type: "revealStep" }]
- ]},
- { id: "nofog", title: "雾失败仍要能看", blurb: "业务可用优先:仍渐显,但承诺改成不保证遮边,并记下边缘可能露出。", steps: [
- ["点击进入系统", { type: "enter" }], ["雾运行失败", { type: "fogFailed" }], ["目标视野可显示", { type: "tilesReady" }],
- ["精细层再亮一档", { type: "revealStep" }]
- ]},
- { id: "tiles", title: "瓦片失败", blurb: "保护已经成立,但目标视野没有影像。应继续隐藏,不要亮出空白再跳变。", steps: [
- ["点击进入系统", { type: "enter" }], ["准备雾", { type: "prepareFog" }], ["把雾强度拉满", { type: "raiseFog" }],
- ["标记保护已成立", { type: "markProtection" }], ["瓦片加载失败", { type: "tilesFailed" }], ["精细层再亮一档", { type: "revealStep" }]
- ]},
- { id: "swap", title: "内部换级", blurb: "精细层完全可见后,地形又换了一级影像。确认整层透明度没有把它淡掉。", steps: [
- ["点击进入系统", { type: "enter" }], ["准备雾", { type: "prepareFog" }], ["把雾强度拉满", { type: "raiseFog" }],
- ["标记保护已成立", { type: "markProtection" }], ["目标视野可显示", { type: "tilesReady" }],
- ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }], ["精细层再亮一档", { type: "revealStep" }],
- ["矩形内部换了一级", { type: "levelSwap" }]
- ]}
- ]
- let state = Transition.createState()
- let previous = null
- let activeScenario = null
- let stepIndex = 0
- function dispatch(action) {
- previous = state
- state = Transition.reduce(state, action)
- render()
- }
- function render() {
- const root = document.querySelector("#state")
- root.replaceChildren()
- const grid = document.createElement("div")
- grid.className = "grid"
- FIELDS.forEach(([key, label, format]) => {
- const card = document.createElement("div")
- card.className = "field" + (previous && previous[key] !== state[key] ? " changed" : "")
- const name = document.createElement("b")
- name.textContent = label
- card.append(name, document.createTextNode(format(state)))
- grid.append(card)
- })
- const notice = document.createElement("p")
- notice.textContent = state.notice
- root.append(grid, notice)
- const changed = previous ? FIELDS.filter(([key]) => previous[key] !== state[key]).map(([, label]) => label) : []
- document.querySelector("#delta").textContent = changed.length ? "这一下变了:" + changed.join("、") : "还没有操作。"
- const refusal = document.querySelector("#refusal")
- refusal.textContent = state.refusal ? "这一下被拒绝:" + state.refusal : ""
- refusal.className = state.refusal ? "bad" : ""
- renderWalk()
- }
- function renderFree() {
- const root = document.querySelector("#free")
- FREE.forEach(([title, actions]) => {
- const row = document.createElement("div")
- row.className = "group"
- const label = document.createElement("span")
- label.textContent = title
- row.append(label)
- actions.forEach(([text, action]) => {
- const button = document.createElement("button")
- button.type = "button"
- button.textContent = text
- button.addEventListener("click", () => { activeScenario = null; dispatch(action) })
- row.append(button)
- })
- root.append(row)
- })
- const reset = document.createElement("button")
- reset.type = "button"
- reset.textContent = "回到初始全球"
- reset.addEventListener("click", () => { activeScenario = null; previous = state; state = Transition.createState(); render() })
- root.append(reset)
- }
- function renderWalk() {
- const tabs = document.querySelector("#tabs")
- tabs.replaceChildren()
- SCENARIOS.forEach((scenario) => {
- const button = document.createElement("button")
- button.type = "button"
- button.textContent = scenario.title
- button.setAttribute("aria-selected", String(activeScenario === scenario.id))
- button.addEventListener("click", () => startScenario(scenario.id))
- tabs.append(button)
- })
- const scenario = SCENARIOS.find((item) => item.id === activeScenario)
- document.querySelector("#blurb").textContent = scenario ? scenario.blurb : "选一个走查会先回到全球初始,再按顺序点。"
- const steps = document.querySelector("#steps")
- steps.replaceChildren()
- if (!scenario) return
- scenario.steps.forEach(([label], index) => {
- const button = document.createElement("button")
- button.type = "button"
- button.textContent = (index + 1) + ". " + label
- button.disabled = index !== stepIndex
- if (index === stepIndex) button.className = "primary"
- button.addEventListener("click", () => {
- dispatch(scenario.steps[index][1])
- stepIndex += 1
- render()
- })
- steps.append(button)
- })
- if (stepIndex >= scenario.steps.length) {
- const done = document.createElement("p")
- done.className = "ok"
- done.textContent = "这一条走完了。拒绝也是结果:看上面有没有「被拒绝」,以及边缘是否可能已露出。"
- steps.append(done)
- }
- }
- function startScenario(id) {
- activeScenario = id
- stepIndex = 0
- previous = null
- state = Transition.createState()
- render()
- }
- renderFree()
- render()
- </script>
- </body>
- </html>
|