修复资源画本转场克隆遮挡顺序的第二次跳变
两个场景的 z-index 数值本身不同(总览 1-3、子画布 25),只搬数值无法保证顺序一致;且只存在于来源里的淡出克隆被排在最后会盖住存活卡片,消失时表现为跳变 改为按目标场景的最终绘制顺序(z-index 升序,同值再按 DOM 顺序)给克隆分配单调层级,淡出项统一压到最底层 快照记录 DOM 序号,补充 resourceBookController 用例断言层级单调与标题栏在卡片之上
This commit is contained in:
@@ -6,6 +6,7 @@ type Snapshot = {
|
||||
rect: { left: number; top: number; width: number; height: number };
|
||||
opacity: number;
|
||||
zIndex: number;
|
||||
order: number;
|
||||
content: HTMLElement;
|
||||
};
|
||||
|
||||
@@ -169,6 +170,7 @@ function capture(root: HTMLElement): Map<string, Snapshot> {
|
||||
'.game-resource-book-scene .game-resource-card, .game-resource-book-scene-titlebar',
|
||||
),
|
||||
);
|
||||
let order = 0;
|
||||
for (const element of elements) {
|
||||
const box = element.getBoundingClientRect();
|
||||
const rect = {
|
||||
@@ -208,6 +210,7 @@ function capture(root: HTMLElement): Map<string, Snapshot> {
|
||||
rect,
|
||||
opacity,
|
||||
zIndex: effectiveZIndex(element, root),
|
||||
order: order++,
|
||||
content: clonePresentation(
|
||||
element.dataset.motionSnapshot
|
||||
? (element.firstElementChild as HTMLElement)
|
||||
@@ -285,23 +288,29 @@ export function createResourceBookTransitionController() {
|
||||
return;
|
||||
}
|
||||
const target = capture(manager);
|
||||
// 先按目标场景的 DOM 顺序排,再补上只存在于来源里的 key。层级相同的卡片
|
||||
// 由 DOM 顺序决定遮挡关系,顺序不一致会让动画结束的瞬间发生跳变。
|
||||
const keys = [
|
||||
...target.keys(),
|
||||
...Array.from(source.keys()).filter((key) => !target.has(key)),
|
||||
];
|
||||
if (!target.size) {
|
||||
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,
|
||||
),
|
||||
...Array.from(source.values()).filter(
|
||||
(entry) => !target.has(entry.key),
|
||||
),
|
||||
];
|
||||
try {
|
||||
for (const key of keys) {
|
||||
const from = source.get(key);
|
||||
const to = target.get(key);
|
||||
const item = to ?? from!;
|
||||
for (const [stackOrder, item] of ordered.entries()) {
|
||||
const from = source.get(item.key);
|
||||
const to = target.get(item.key);
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.dataset.motionSnapshot = key;
|
||||
wrapper.dataset.motionSnapshot = item.key;
|
||||
Object.assign(wrapper.style, {
|
||||
position: 'absolute',
|
||||
left: `${item.rect.left}px`,
|
||||
@@ -310,7 +319,7 @@ export function createResourceBookTransitionController() {
|
||||
height: `${item.rect.height}px`,
|
||||
transformOrigin: '0 0',
|
||||
pointerEvents: 'none',
|
||||
zIndex: String(item.zIndex),
|
||||
zIndex: String(stackOrder + 1),
|
||||
});
|
||||
wrapper.append(item.content);
|
||||
layer.append(wrapper);
|
||||
|
||||
@@ -173,8 +173,10 @@ describe('resource book FLIP controller', () => {
|
||||
const cardWrapper = wrappers.find((element) =>
|
||||
element.dataset.motionSnapshot?.startsWith('card:'),
|
||||
);
|
||||
expect(Number(titleWrapper?.style.zIndex)).toBe(30);
|
||||
expect(Number(cardWrapper?.style.zIndex)).toBe(25);
|
||||
// 克隆按目标场景的最终绘制顺序分配单调层级:标题栏必须在卡片之上。
|
||||
expect(Number(titleWrapper?.style.zIndex)).toBeGreaterThan(
|
||||
Number(cardWrapper?.style.zIndex),
|
||||
);
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
@@ -215,6 +217,10 @@ describe('resource book FLIP controller', () => {
|
||||
'card:b',
|
||||
'card:a',
|
||||
]);
|
||||
// 同层级时靠 DOM 顺序决定遮挡:目标顺序里 b 在前,b 的层级必须更低。
|
||||
expect(Number(wrappers[0]?.style.zIndex)).toBeLessThan(
|
||||
Number(wrappers[1]?.style.zIndex),
|
||||
);
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user