修复资源画本转场克隆的遮挡顺序跳变
层级相同的卡片由 DOM 顺序决定遮挡,而转场层原先按来源 key 在前追加克隆,和动画结束后的目标场景顺序不一致,结束瞬间会跳变 改为先按目标场景 DOM 顺序排列,再补只存在于来源里的 key 补充 resourceBookController 用例覆盖来源与目标顺序不同时的克隆顺序
This commit is contained in:
@@ -285,7 +285,12 @@ export function createResourceBookTransitionController() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const target = capture(manager);
|
const target = capture(manager);
|
||||||
const keys = new Set([...source.keys(), ...target.keys()]);
|
// 先按目标场景的 DOM 顺序排,再补上只存在于来源里的 key。层级相同的卡片
|
||||||
|
// 由 DOM 顺序决定遮挡关系,顺序不一致会让动画结束的瞬间发生跳变。
|
||||||
|
const keys = [
|
||||||
|
...target.keys(),
|
||||||
|
...Array.from(source.keys()).filter((key) => !target.has(key)),
|
||||||
|
];
|
||||||
if (!target.size) {
|
if (!target.size) {
|
||||||
settle();
|
settle();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -178,6 +178,46 @@ describe('resource book FLIP controller', () => {
|
|||||||
controller.dispose();
|
controller.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('appends clones in the target DOM order so equal z-index cards keep their occlusion', () => {
|
||||||
|
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);
|
||||||
|
|
||||||
|
const wrappers = Array.from(
|
||||||
|
root.querySelectorAll<HTMLElement>('[data-motion-snapshot]'),
|
||||||
|
);
|
||||||
|
expect(wrappers.map((element) => element.dataset.motionSnapshot)).toEqual([
|
||||||
|
'card:b',
|
||||||
|
'card:a',
|
||||||
|
]);
|
||||||
|
controller.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
it('retargets from the current overlay geometry and ignores old completion', async () => {
|
it('retargets from the current overlay geometry and ignores old completion', async () => {
|
||||||
const { root, measure, flights } = setup();
|
const { root, measure, flights } = setup();
|
||||||
const controller = createResourceBookTransitionController();
|
const controller = createResourceBookTransitionController();
|
||||||
|
|||||||
Reference in New Issue
Block a user