资源画布首屏加载增加转圈提示
进入资源画布到首屏预览落地之间画布是空的,新增居中加载态:Loader2 转圈加文案,role=status 可被读屏播报 加载判定抽到 resourceBookIsLoading:有资源且布局未就绪或首屏预览一张都没落地时显示;预览失败同样视为落地,避免一直转圈 补充 resourceBookModel 加载态用例
This commit is contained in:
@@ -5634,6 +5634,21 @@ iframe.preview-frame {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-resource-book-loading {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 40;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
background: rgb(255 253 250 / 78%);
|
||||||
|
color: #a08073;
|
||||||
|
font-size: 13px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.game-resource-book-manager[data-book-motion='running']
|
.game-resource-book-manager[data-book-motion='running']
|
||||||
.game-resource-book-scene-world {
|
.game-resource-book-scene-world {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
Image,
|
Image,
|
||||||
Info,
|
Info,
|
||||||
ListFilter,
|
ListFilter,
|
||||||
|
Loader2,
|
||||||
Maximize2,
|
Maximize2,
|
||||||
Minus,
|
Minus,
|
||||||
Music2,
|
Music2,
|
||||||
@@ -98,6 +99,7 @@ import {
|
|||||||
} from './resourceBookLayout';
|
} from './resourceBookLayout';
|
||||||
import {
|
import {
|
||||||
initialResourceBookState,
|
initialResourceBookState,
|
||||||
|
resourceBookIsLoading,
|
||||||
resourceBookReducer,
|
resourceBookReducer,
|
||||||
resourceBookSceneCategory,
|
resourceBookSceneCategory,
|
||||||
resourceBookShowsChildCards,
|
resourceBookShowsChildCards,
|
||||||
@@ -2189,6 +2191,36 @@ export default function ProjectDevelopmentView({
|
|||||||
sortMode,
|
sortMode,
|
||||||
visibleCategoryOrder,
|
visibleCategoryOrder,
|
||||||
]);
|
]);
|
||||||
|
const resourceBookOverviewReady = useMemo(
|
||||||
|
() =>
|
||||||
|
visibleCategoryOrder.every((category) => {
|
||||||
|
const rect = resourceBookOverviewRects.get(category);
|
||||||
|
return Boolean(rect && rect.width > 0 && rect.height > 0);
|
||||||
|
}),
|
||||||
|
[resourceBookOverviewRects, visibleCategoryOrder],
|
||||||
|
);
|
||||||
|
const resourceBookSettledPreviewCount = useMemo(() => {
|
||||||
|
let settled = 0;
|
||||||
|
for (const resource of visibleResources) {
|
||||||
|
const identity = resourceCardPreviews.identityByResourceId.get(
|
||||||
|
resource.id,
|
||||||
|
);
|
||||||
|
if (!identity) continue;
|
||||||
|
const status = resourceCardPreviews.previews.get(identity)?.status;
|
||||||
|
if (status === 'loaded' || status === 'failed') settled += 1;
|
||||||
|
}
|
||||||
|
return settled;
|
||||||
|
}, [
|
||||||
|
resourceCardPreviews.identityByResourceId,
|
||||||
|
resourceCardPreviews.previews,
|
||||||
|
visibleResources,
|
||||||
|
]);
|
||||||
|
const resourceBookLoading = resourceBookIsLoading({
|
||||||
|
resourceCount: visibleResources.length,
|
||||||
|
overviewReady: resourceBookOverviewReady,
|
||||||
|
cardsReady: sortMode !== 'dependency' || resourceGraphInitializationReady,
|
||||||
|
settledPreviewCount: resourceBookSettledPreviewCount,
|
||||||
|
});
|
||||||
const setResourceCanvasViewport = useCallback(
|
const setResourceCanvasViewport = useCallback(
|
||||||
(
|
(
|
||||||
category: ResourceCategory,
|
category: ResourceCategory,
|
||||||
@@ -5590,6 +5622,20 @@ export default function ProjectDevelopmentView({
|
|||||||
onWheel={handleResourceBookWheel}
|
onWheel={handleResourceBookWheel}
|
||||||
/>
|
/>
|
||||||
<ResourceBookTransitionLayer />
|
<ResourceBookTransitionLayer />
|
||||||
|
{resourceBookLoading ? (
|
||||||
|
<div
|
||||||
|
className="game-resource-book-loading"
|
||||||
|
role="status"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
<Loader2
|
||||||
|
size={22}
|
||||||
|
aria-hidden="true"
|
||||||
|
className="animate-spin"
|
||||||
|
/>
|
||||||
|
<span>正在加载资源画布…</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<div className="game-resource-book-zoom">
|
<div className="game-resource-book-zoom">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -78,3 +78,24 @@ export function resourceBookShowsOverviewCards(state: ResourceBookState) {
|
|||||||
export function resourceBookShowsChildCards(state: ResourceBookState) {
|
export function resourceBookShowsChildCards(state: ResourceBookState) {
|
||||||
return state.view === 'child';
|
return state.view === 'child';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进入资源画布时,卡片布局与首屏预览都要等一会儿,这段时间画布是空的。
|
||||||
|
* 只要还有资源、但布局未就绪或首屏预览一个都没落地,就显示加载态。
|
||||||
|
* 预览失败也算落地,避免一直转圈。
|
||||||
|
*/
|
||||||
|
export function resourceBookIsLoading({
|
||||||
|
resourceCount,
|
||||||
|
overviewReady,
|
||||||
|
cardsReady,
|
||||||
|
settledPreviewCount,
|
||||||
|
}: {
|
||||||
|
resourceCount: number;
|
||||||
|
overviewReady: boolean;
|
||||||
|
cardsReady: boolean;
|
||||||
|
settledPreviewCount: number;
|
||||||
|
}) {
|
||||||
|
if (resourceCount <= 0) return false;
|
||||||
|
if (!cardsReady || !overviewReady) return true;
|
||||||
|
return settledPreviewCount <= 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
initialResourceBookState,
|
initialResourceBookState,
|
||||||
|
resourceBookIsLoading,
|
||||||
resourceBookReducer,
|
resourceBookReducer,
|
||||||
resourceBookSceneCategory,
|
resourceBookSceneCategory,
|
||||||
resourceBookShowsOverviewCards,
|
resourceBookShowsOverviewCards,
|
||||||
@@ -103,3 +104,30 @@ describe('resource book state machine', () => {
|
|||||||
).toEqual(switched);
|
).toEqual(switched);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('resource book loading state', () => {
|
||||||
|
const ready = {
|
||||||
|
resourceCount: 3,
|
||||||
|
overviewReady: true,
|
||||||
|
cardsReady: true,
|
||||||
|
settledPreviewCount: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('never blocks an empty project', () => {
|
||||||
|
expect(resourceBookIsLoading({ ...ready, resourceCount: 0 })).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads while the layout or first preview is still pending', () => {
|
||||||
|
expect(resourceBookIsLoading({ ...ready, cardsReady: false })).toBe(true);
|
||||||
|
expect(resourceBookIsLoading({ ...ready, overviewReady: false })).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(resourceBookIsLoading({ ...ready, settledPreviewCount: 0 })).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stops once a first preview settles', () => {
|
||||||
|
expect(resourceBookIsLoading(ready)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user