fix: streamline world 3d viewer overlay
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-04-29 11:42:49 +08:00
parent 9837ff0b05
commit 93cc86aa54

View File

@ -730,6 +730,7 @@ function WorldSplatScene({
onLoadStateChange,
onRevealComplete,
resetViewToken,
showInfoOverlay = true,
}: {
splatUrl: string;
revealPhase?: SplatLoadingPhase;
@ -742,6 +743,7 @@ function WorldSplatScene({
onLoadStateChange?: (state: "ready" | "error") => void;
onRevealComplete?: () => void;
resetViewToken?: number;
showInfoOverlay?: boolean;
}) {
const camera = useThree((state) => state.camera);
const renderer = useThree((state) => state.gl);
@ -749,6 +751,7 @@ function WorldSplatScene({
const hasAppliedInitialViewRef = useRef(false);
const lastCameraPoseRef = useRef<WorldCameraPose | null>(null);
const lastResetViewTokenRef = useRef<number | undefined>(resetViewToken);
const lastFirstPersonRef = useRef(firstPerson);
const revealCompletedRef = useRef(false);
const [controlsMountVersion, setControlsMountVersion] = useState(0);
const [particleCloudData, setParticleCloudData] = useState<SplatParticleCloudData | null>(null);
@ -869,6 +872,24 @@ function WorldSplatScene({
});
}, [camera, groundPlaneOffset, resetViewToken, splatMesh]);
useEffect(() => {
if (!hasAppliedInitialViewRef.current) {
lastFirstPersonRef.current = firstPerson;
return;
}
if (!lastFirstPersonRef.current && firstPerson) {
lastCameraPoseRef.current = positionCameraInsideWorld({
camera,
controls: orbitControlsRef.current,
groundPlaneOffset,
splatMesh,
});
}
lastFirstPersonRef.current = firstPerson;
}, [camera, firstPerson, groundPlaneOffset, splatMesh]);
useEffect(() => {
if (!orbitControlsRef.current || !lastCameraPoseRef.current) return;
@ -1006,20 +1027,24 @@ function WorldSplatScene({
<Html className="pointer-events-none" fullscreen>
<div className="flex h-full w-full flex-col justify-between p-4">
<div className="flex items-start justify-between">
<div className="flex gap-2">
<Badge className="border-cyan-500/30 bg-cyan-500/10 text-cyan-200" variant="outline">
SPZ
</Badge>
{semanticFacts.map((fact) => (
<Badge
className="border-white/10 bg-black/50 text-zinc-200"
key={fact.label}
variant="outline"
>
{fact.label} {fact.value}
{showInfoOverlay ? (
<div className="flex gap-2">
<Badge className="border-cyan-500/30 bg-cyan-500/10 text-cyan-200" variant="outline">
SPZ
</Badge>
))}
</div>
{semanticFacts.map((fact) => (
<Badge
className="border-white/10 bg-black/50 text-zinc-200"
key={fact.label}
variant="outline"
>
{fact.label} {fact.value}
</Badge>
))}
</div>
) : (
<div />
)}
{interactionMode === "hotspot" ? (
<div className="rounded-full border border-yellow-400/30 bg-black/65 px-3 py-1 text-[11px] text-yellow-100">
点击场景放置热点
@ -1099,6 +1124,7 @@ function WorldSplatViewer({
resetViewToken,
minimalFrame = false,
onCaptureReady,
showInfoOverlay = true,
}: {
splatUrl?: string | null;
worldUrl?: string | null;
@ -1112,6 +1138,7 @@ function WorldSplatViewer({
semanticsMetadata?: Record<string, unknown> | null;
resetViewToken?: number;
onCaptureReady?: (capture: (() => Promise<Blob | null>) | null) => void;
showInfoOverlay?: boolean;
}) {
const [loadState, setLoadState] = useState<"loading" | "ready" | "error">("loading");
const [overlayPhase, setOverlayPhase] = useState<SplatLoadingPhase>("loading");
@ -1187,6 +1214,7 @@ function WorldSplatViewer({
revealPhase={overlayPhase}
resetViewToken={resetViewToken}
semanticsMetadata={semanticsMetadata}
showInfoOverlay={showInfoOverlay}
splatUrl={splatUrl}
/>
</Canvas>
@ -1316,6 +1344,7 @@ export function World3DViewer({
minimalFrame={minimalFrame}
resetViewToken={resetViewToken}
semanticsMetadata={semanticsMetadata}
showInfoOverlay={!minimalFrame}
splatUrl={splatUrl}
worldUrl={worldUrl}
/>