修复资源画本飞行期间遮挡沿用错误一侧顺序
卡片真正重叠的是堆叠那一侧:进入时重叠在总览摞(来源),返回时重叠在摞(目标)。原先统一按目标顺序排,进入时就用错了顺序,堆叠里下面两张飞到上面,结束才回到下方 play 增加 stacking 参数:进入用来源顺序,返回用目标顺序;不在该侧的卡片压到最底层 补充 resourceBookController 用例覆盖来源顺序
This commit is contained in:
@@ -2550,6 +2550,8 @@ export default function ProjectDevelopmentView({
|
||||
token: resourceBookState.token,
|
||||
});
|
||||
},
|
||||
// 进入时重叠在总览摞上,沿用来源顺序;返回时重叠在摞上,沿用目标顺序。
|
||||
resourceBookState.phase === 'returning-main' ? 'target' : 'source',
|
||||
);
|
||||
}, [resourceBookState.token, resourceBookState.phase, mode]);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 = `<div class="game-resource-book-scene"><div class="game-resource-book-scene-world">
|
||||
<div class="game-resource-book-scene-card"><div class="game-resource-card" data-resource-card-id="a"></div></div>
|
||||
<div class="game-resource-book-scene-card"><div class="game-resource-card" data-resource-card-id="b"></div></div>
|
||||
</div></div><div class="game-resource-book-transition-layer" inert aria-hidden="true"></div>`;
|
||||
document.body.append(root);
|
||||
vi.spyOn(root, 'getBoundingClientRect').mockReturnValue(rect(0, 0, 800, 600));
|
||||
for (const element of root.querySelectorAll<HTMLElement>(
|
||||
'.game-resource-card',
|
||||
)) {
|
||||
vi.spyOn(element, 'getBoundingClientRect').mockReturnValue(rect());
|
||||
}
|
||||
|
||||
const controller = createResourceBookTransitionController();
|
||||
const token = controller.begin(root);
|
||||
const cardWrapper = root
|
||||
.querySelector<HTMLElement>('[data-resource-card-id="a"]')!
|
||||
.closest<HTMLElement>('.game-resource-book-scene-card')!;
|
||||
cardWrapper.parentElement!.append(cardWrapper);
|
||||
controller.play(root, token, () => undefined, 'source');
|
||||
|
||||
const wrappers = Array.from(
|
||||
root.querySelectorAll<HTMLElement>('[data-motion-snapshot]'),
|
||||
);
|
||||
expect(wrappers.map((element) => element.dataset.motionSnapshot)).toEqual([
|
||||
'card:a',
|
||||
'card:b',
|
||||
]);
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
it('retargets from the current overlay geometry and ignores old completion', async () => {
|
||||
const { root, measure, flights } = setup();
|
||||
const controller = createResourceBookTransitionController();
|
||||
|
||||
Reference in New Issue
Block a user