diff --git a/apps/ai-game-creator-shell/src/view/project-development/index.tsx b/apps/ai-game-creator-shell/src/view/project-development/index.tsx index c470d3fba..493959669 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/index.tsx +++ b/apps/ai-game-creator-shell/src/view/project-development/index.tsx @@ -2550,6 +2550,8 @@ export default function ProjectDevelopmentView({ token: resourceBookState.token, }); }, + // 进入时重叠在总览摞上,沿用来源顺序;返回时重叠在摞上,沿用目标顺序。 + resourceBookState.phase === 'returning-main' ? 'target' : 'source', ); }, [resourceBookState.token, resourceBookState.phase, mode]); diff --git a/apps/ai-game-creator-shell/src/view/project-development/resourceBookController.ts b/apps/ai-game-creator-shell/src/view/project-development/resourceBookController.ts index 13acf00ee..9af169220 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/resourceBookController.ts +++ b/apps/ai-game-creator-shell/src/view/project-development/resourceBookController.ts @@ -271,7 +271,12 @@ export function createResourceBookTransitionController() { invalidate, settle, isCurrent: (expected: number) => token === expected, - play(manager: HTMLElement, expected: number, done: () => void) { + play( + manager: HTMLElement, + expected: number, + done: () => void, + stacking: 'source' | 'target' = 'target', + ) { if (token !== expected) return; root = manager; completion = done; @@ -292,19 +297,28 @@ export function createResourceBookTransitionController() { settle(); return; } - // 两个场景的 z-index 数值本身不同(总览 1–3、子画布 25),只搬数值无法保证 - // 顺序一致。按目标场景的最终绘制顺序(z-index 升序,同值再按 DOM 顺序) - // 给克隆分配单调层级,动画期间的遮挡关系就与结束后的静止状态完全一致; - // 只存在于来源里的淡出项统一压到最底层,不会盖住存活卡片。 - const ordered = [ - ...Array.from(target.values()).sort( - (left, right) => - left.zIndex - right.zIndex || left.order - right.order, - ), + // 两个场景的 z-index 数值域不同(总览 1–3、子画布 25),只搬数值保证不了顺序。 + // 卡片真正重叠的是"堆叠"那一侧:进入时重叠在总览摞(来源),返回时重叠在摞 + // (目标)。按该侧 z-index 升序、同值再按 DOM 顺序给克隆分配单调层级,飞行期间 + // 的遮挡就和用户眼前那一摞一致;不在该侧的卡片压到最底层。 + const reference = stacking === 'source' ? source : target; + const entries = [ + ...Array.from(target.values()), ...Array.from(source.values()).filter( (entry) => !target.has(entry.key), ), ]; + const primary = entries.filter((entry) => reference.has(entry.key)); + const secondary = entries.filter((entry) => !reference.has(entry.key)); + primary.sort((left, right) => { + const leftReference = reference.get(left.key)!; + const rightReference = reference.get(right.key)!; + return ( + leftReference.zIndex - rightReference.zIndex || + leftReference.order - rightReference.order + ); + }); + const ordered = [...primary, ...secondary]; try { for (const [stackOrder, item] of ordered.entries()) { const from = source.get(item.key); diff --git a/apps/ai-game-creator-shell/tests/resourceBookController.test.ts b/apps/ai-game-creator-shell/tests/resourceBookController.test.ts index 05801ec23..933787c9f 100644 --- a/apps/ai-game-creator-shell/tests/resourceBookController.test.ts +++ b/apps/ai-game-creator-shell/tests/resourceBookController.test.ts @@ -224,6 +224,46 @@ describe('resource book FLIP controller', () => { controller.dispose(); }); + it('keeps the source stack order while flying out of the overview', () => { + Object.defineProperty(Element.prototype, 'animate', { + configurable: true, + writable: true, + value: () => ({ + finished: Promise.resolve({} as Animation), + cancel: () => undefined, + }), + }); + const root = document.createElement('div'); + root.innerHTML = `