取消资源画本返回主画布的二次平移动画
新增 returning-main 阶段,区分返回主画布与进入子画布的 transform 过渡。 返回期间取消残留分页事件和进行中的子画布切换,避免返回流程被重新改写。 补充状态机与 appSurface 回归测试,并同步技术方案文档。
This commit is contained in:
@@ -5192,11 +5192,14 @@ iframe.preview-frame {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.game-resource-book-scene--entering .game-resource-book-scene-world,
|
||||
.game-resource-book-scene--returning .game-resource-book-scene-world {
|
||||
.game-resource-book-scene--entering .game-resource-book-scene-world {
|
||||
transition: transform 420ms cubic-bezier(0.2, 0.78, 0.2, 1);
|
||||
}
|
||||
|
||||
.game-resource-book-scene--returning-main .game-resource-book-scene-world {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.game-resource-book-scene--child {
|
||||
pointer-events: auto;
|
||||
}
|
||||
@@ -5471,11 +5474,6 @@ iframe.preview-frame {
|
||||
linear-gradient(145deg, #fffdfa 0%, #fff8f2 100%);
|
||||
}
|
||||
|
||||
.game-resource-book-manager--entering .game-resource-book-main,
|
||||
.game-resource-book-manager--returning .game-resource-book-main {
|
||||
transition: transform 420ms cubic-bezier(0.2, 0.78, 0.2, 1);
|
||||
}
|
||||
|
||||
.game-resource-book-main-heading {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
@@ -2640,6 +2640,13 @@ export default function ProjectDevelopmentView({
|
||||
return;
|
||||
}
|
||||
if (resourceBookState.phase === 'returning') {
|
||||
if (!resourceBookState.pendingCategory) {
|
||||
// A return-to-main transition has no destination category. Ignore
|
||||
// any wheel step that was already queued before the collapse click;
|
||||
// otherwise it can resurrect a child-enter transition and append a
|
||||
// second viewport movement after the return leg.
|
||||
return;
|
||||
}
|
||||
// A wheel gesture can arrive while a click-driven book transition is
|
||||
// still in flight. Retarget the transition through the same tokenized
|
||||
// path so the old timer cannot restore a stale category.
|
||||
@@ -2661,6 +2668,7 @@ export default function ProjectDevelopmentView({
|
||||
cancelResourceCanvasPan,
|
||||
cancelResourceCardDrag,
|
||||
openResourceBookChild,
|
||||
resourceBookState.pendingCategory,
|
||||
resourceBookState.phase,
|
||||
resourcePageCategories,
|
||||
],
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import type { ProjectResourceCategory } from './resourceProjectionModel';
|
||||
|
||||
export type ResourceBookCategory = ProjectResourceCategory;
|
||||
export type ResourceBookPhase = 'idle' | 'entering' | 'returning';
|
||||
export type ResourceBookPhase =
|
||||
| 'idle'
|
||||
| 'entering'
|
||||
| 'returning'
|
||||
| 'returning-main';
|
||||
|
||||
export type ResourceBookState = {
|
||||
view: 'main' | 'child';
|
||||
@@ -74,7 +78,7 @@ export function resourceBookReducer(
|
||||
case 'finish-transition':
|
||||
return state.token === action.token ? { ...state, phase: 'idle' } : state;
|
||||
case 'return-to-main':
|
||||
if (state.view !== 'child' || state.phase === 'returning') {
|
||||
if (state.view !== 'child') {
|
||||
return state;
|
||||
}
|
||||
return {
|
||||
@@ -97,11 +101,10 @@ export function resourceBookReducer(
|
||||
view: 'main',
|
||||
category: null,
|
||||
pendingCategory: null,
|
||||
// Keep the return leg in the animated phase while the scene
|
||||
// viewport transitions from the child viewport back to the
|
||||
// main viewport. The controller dispatches finish-transition
|
||||
// after the same animation window used by child entry.
|
||||
phase: 'entering',
|
||||
// Returning to the main canvas is already completed by the
|
||||
// shared-card shrink leg. Keep a distinct phase so the main
|
||||
// viewport does not receive a second transform transition.
|
||||
phase: 'returning-main',
|
||||
}
|
||||
: state;
|
||||
case 'reset':
|
||||
|
||||
@@ -786,6 +786,31 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
{ name: '收起资源' },
|
||||
),
|
||||
);
|
||||
fireEvent.click(
|
||||
within(
|
||||
document.querySelector<HTMLElement>(
|
||||
'.game-resource-book-scene-titlebar.is-active',
|
||||
) as HTMLElement,
|
||||
).getByRole('button', { name: '收起资源' }),
|
||||
);
|
||||
await act(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 340));
|
||||
});
|
||||
expect(
|
||||
screen
|
||||
.getByRole('region', { name: '资源主画布' })
|
||||
.closest('[data-resource-book-view]')
|
||||
?.getAttribute('data-resource-book-transition'),
|
||||
).toBe('returning-main');
|
||||
await act(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 540));
|
||||
});
|
||||
expect(
|
||||
screen
|
||||
.getByRole('region', { name: '资源主画布' })
|
||||
.closest('[data-resource-book-view]')
|
||||
?.getAttribute('data-resource-book-transition'),
|
||||
).toBe('idle');
|
||||
await openResourceBookCategory('设计文档');
|
||||
|
||||
dispatchPageWheel(dependencyCanvas);
|
||||
|
||||
@@ -73,6 +73,62 @@ describe('resource book state machine', () => {
|
||||
expect(resourceBookSceneCategory(overview)).toBe('art');
|
||||
});
|
||||
|
||||
it('marks a return to the main canvas separately from a child enter transition', () => {
|
||||
const entering = resourceBookReducer(initialResourceBookState, {
|
||||
type: 'open-category',
|
||||
category: 'document',
|
||||
token: 1,
|
||||
});
|
||||
const child = resourceBookReducer(entering, {
|
||||
type: 'commit-enter',
|
||||
token: 1,
|
||||
});
|
||||
const returning = resourceBookReducer(child, {
|
||||
type: 'return-to-main',
|
||||
token: 2,
|
||||
});
|
||||
const main = resourceBookReducer(returning, {
|
||||
type: 'commit-return',
|
||||
token: 2,
|
||||
});
|
||||
|
||||
expect(main).toMatchObject({
|
||||
view: 'main',
|
||||
category: null,
|
||||
pendingCategory: null,
|
||||
phase: 'returning-main',
|
||||
});
|
||||
});
|
||||
|
||||
it('lets an explicit collapse cancel an in-flight child switch', () => {
|
||||
const entering = resourceBookReducer(initialResourceBookState, {
|
||||
type: 'open-category',
|
||||
category: 'document',
|
||||
token: 1,
|
||||
});
|
||||
const child = resourceBookReducer(entering, {
|
||||
type: 'commit-enter',
|
||||
token: 1,
|
||||
});
|
||||
const switching = resourceBookReducer(child, {
|
||||
type: 'open-category',
|
||||
category: 'art',
|
||||
token: 2,
|
||||
});
|
||||
const main = resourceBookReducer(switching, {
|
||||
type: 'return-to-main',
|
||||
token: 3,
|
||||
});
|
||||
|
||||
expect(main).toMatchObject({
|
||||
view: 'child',
|
||||
category: 'document',
|
||||
pendingCategory: null,
|
||||
phase: 'returning',
|
||||
token: 3,
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores stale transition callbacks', () => {
|
||||
const entering = resourceBookReducer(initialResourceBookState, {
|
||||
type: 'open-category',
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
子画布改为固定视口加可无限平移的 world 层:视口本身保持 `overflow: hidden`,平移和缩放只作用于 `.game-resource-book-scene-world`,不再让内容容器的边界限制拖动范围。首次 fit(包括隐藏分页画布提前完成的 fit)统一保证比例不低于 `1:1`,避免子画布展开后卡片缩成不可读的小块;用户后续主动缩放或平移的视口仍按每个分类独立保留。新增 viewport 回归测试覆盖初始可读比例和超出内容边界双向平移。
|
||||
|
||||
返回主画布时使用独立的 `returning-main` 阶段:卡片先完成子画布到主画布预览位的缩回,随后主画布直接进入稳定布局,不再重复播放 world/main viewport 的平移动画;子画布之间切换仍使用原有的返回后进入动画。
|
||||
|
||||
## 2026-09-04 资源画本 UI 阶段补充
|
||||
|
||||
资源工作台当前产品方向调整为“主画布 + 子画布”的统一画本模型。主画布展示各子画布的缩略卡片,不直接展开真实资源内容;缩略卡片内部按资源类型显示有限层叠卡片,最多展示三层,更多资源以虚化卡片提示。进入子画布时使用共享元素转场:标题栏和资源卡片沿用同一组 DOM 元素,从缩略卡片的原始位置移动、展开到子画布中的目标位置,不允许先播放独立放大动画再硬切另一套内容;主画布预览和子画布展开态必须复用同一套卡片视觉与标题栏结构。进入子画布时在标题栏提供缩回主画布的缩放按钮;从一个子画布切换到另一个子画布时必须先完成缩回主画布,再进入目标子画布。
|
||||
|
||||
Reference in New Issue
Block a user