fix: 统一馆内三维模型返回初始状态

This commit is contained in:
lyf
2026-07-14 21:57:31 +08:00
parent d813f0b375
commit 84fbbc0694
8 changed files with 319 additions and 168 deletions

View File

@@ -478,6 +478,7 @@ let resizeObserver: ResizeObserver | null = null
let isDisposed = false
let pendingTargetFocus: TargetPoiFocusRequest | null = null
let targetFocusQueue: Promise<unknown> = Promise.resolve()
let targetFocusGeneration = 0
let modelLoadVersion = 0
let pendingRequestedFloorId = ''
let floorSwitchLoadToken = 0
@@ -515,6 +516,7 @@ let defaultFloorPreloadSeq = 0
let firstModelLoadStartedAt = 0
let firstModelVisibleReported = false
let initialModelSettled = false
let initialGuideState: { floorId: string; camera: CameraSnapshot } | null = null
const cameraTweenEnabled = true
const cameraTweenDurationMs = 480
@@ -3519,6 +3521,8 @@ const getVisualStabilityReport = () => ({
anchors: getProjectedBuildingAnchors(),
activeView: activeView.value,
floorId: currentFloor.value,
activeFocusPoiId: activeFocusPoiId.value,
hasPendingTargetFocus: Boolean(pendingTargetFocus),
isCameraTweening: Boolean(cameraTween),
poiFocusCameraAnimationCount,
cameraSnapshotRestoreCount,
@@ -3558,7 +3562,8 @@ const installVisualStabilityDiagnostics = () => {
queueTargetFocus(request)
return targetFocusQueue
},
clearTargetFocus
clearTargetFocus,
resetToInitialState
}
}
@@ -5144,6 +5149,7 @@ const emitTargetFocus = (
}
const clearTargetFocus = () => {
targetFocusGeneration += 1
pendingTargetFocus = null
clearMapSelection(false)
}
@@ -5176,7 +5182,10 @@ const focusCameraOnPoi = (poi: RenderPoi) => {
moveCameraTo(nextPosition, target, { reason: 'poi-focus' })
}
const focusTargetPoi = async (request: TargetPoiFocusRequest) => {
const isCurrentTargetFocus = (generation: number) => generation === targetFocusGeneration
const focusTargetPoi = async (request: TargetPoiFocusRequest, generation: number) => {
if (!isCurrentTargetFocus(generation)) return false
if (!request.poiId || !request.floorId) {
emitTargetFocus(request, 'missing', '目标位置数据不完整')
return false
@@ -5198,12 +5207,15 @@ const focusTargetPoi = async (request: TargetPoiFocusRequest) => {
suppressProgress: true,
detachPoiBeforeLoad: true
})
if (!isCurrentTargetFocus(generation)) return false
isLoading.value = false
emit('floorChange', request.floorId)
} else if (shouldRenderPoiMarkers.value && !getPoiSprites().length) {
await loadFloorPOIs(floor, modelLoadVersion)
if (!isCurrentTargetFocus(generation)) return false
}
if (!isCurrentTargetFocus(generation)) return false
const poi = findLoadedPoi(request.poiId) || addFocusMarkerFromRequest(request) || createPoiFromFocusRequest(request)
if (!poi?.positionGltf) {
@@ -5244,11 +5256,12 @@ const queueTargetFocus = (request: TargetPoiFocusRequest | null) => {
if (!isSceneReadyForTargetFocus()) return
const nextRequest = pendingTargetFocus
const requestGeneration = targetFocusGeneration
if (!nextRequest) return
pendingTargetFocus = null
targetFocusQueue = targetFocusQueue
.then(() => focusTargetPoi(nextRequest))
.then(() => focusTargetPoi(nextRequest, requestGeneration))
.finally(() => {
if (pendingTargetFocus) {
queueTargetFocus(pendingTargetFocus)
@@ -5321,6 +5334,12 @@ const init3DScene = async () => {
installVisualStabilityDiagnostics()
await loadModelPackage()
// The package, not a hard-coded floor, determines the stable first-ready baseline.
initialGuideState = {
floorId: currentFloor.value,
camera: captureCameraSnapshot()
}
setProgress(100, '馆内三维模型加载完成')
initialModelSettled = true
isLoading.value = false
@@ -5440,6 +5459,52 @@ const showMultiFloor = async () => {
}
}
// Restores business and camera state on the existing scene. It never initializes WebGL or
// reloads the model package, so closing a detail page cannot remount ThreeMap.
const resetToInitialState = async () => {
clearTargetFocus()
invalidateModelLoads()
adjacentPreloadSeq += 1
defaultFloorPreloadSeq += 1
cancelCameraTween()
clearProgrammaticCameraTimer()
clearRoutePreview()
selectedPOI.value = null
activeFocusPoiId.value = ''
disposeFocusLabel()
disposeFocusPulse()
disposeFocusBase()
clearFocusHallHighlight()
updatePoiMarkerFocus()
autoSwitchStateMachine.reset('overview')
resetInteractionGateState()
const baseline = initialGuideState
if (!baseline || !scene || !camera || !controls || loadError.value) return false
isLoading.value = true
try {
currentFloor.value = baseline.floorId
if (activeView.value !== 'overview') {
await loadOverview({ cameraSnapshot: baseline.camera })
}
if (!scene || !isCurrentModelLoad(modelLoadVersion)) return false
restoreCameraSnapshot(baseline.camera)
currentFloor.value = baseline.floorId
activeView.value = 'overview'
autoSwitchStateMachine.reset('overview')
refreshPoiVisibilityByDistance()
return true
} catch (error) {
if (!isStaleModelLoadError(error)) {
console.error('恢复馆内导览初始状态失败:', error)
}
return false
} finally {
isLoading.value = false
}
}
const retryLoad = () => {
init3DScene()
}
@@ -5530,6 +5595,7 @@ defineExpose({
},
clearSelection: clearMapSelection,
clearRoute: clearRoutePreview,
resetToInitialState,
clearNavigation: () => clearMapSelection(false),
disableAutoSwitchTemporarily
})

View File

@@ -460,6 +460,7 @@ const indoorRendererRef = ref<{
zoomCamera?: (direction: 'in' | 'out', options?: { source?: 'button' | 'gesture' }) => void
clearSelection?: (shouldEmit?: boolean) => void
clearRoute?: () => Promise<void> | void
resetToInitialState?: () => Promise<boolean> | boolean
disableAutoSwitchTemporarily?: (durationMs: number) => void
} | null>(null)
@@ -779,7 +780,8 @@ defineExpose({
clearRoute: () => {
indoorRendererRef.value?.clearRoute?.()
},
showOverview: handleShowOverview
showOverview: handleShowOverview,
resetToInitialState: () => indoorRendererRef.value?.resetToInitialState?.() || false
})
</script>