From f2ee41fe440d4cb2ad7e7574c3c5be8d265872cb Mon Sep 17 00:00:00 2001 From: lyf <2514544224@qq.com> Date: Mon, 13 Jul 2026 19:51:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=A5=BC=E5=B1=82=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=90=8E=E6=97=A0=E6=B3=95=E8=BF=94=E5=9B=9E=E5=BB=BA?= =?UTF-8?q?=E7=AD=91=E5=A4=96=E8=A7=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/map/ThreeMap.vue | 17 +++++++++++++++++ tests/e2e/three-map-camera-stability.spec.ts | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/components/map/ThreeMap.vue b/src/components/map/ThreeMap.vue index fa9467d..bcd1d38 100644 --- a/src/components/map/ThreeMap.vue +++ b/src/components/map/ThreeMap.vue @@ -543,6 +543,8 @@ const poiAmbientLabelServiceSpacingPx = 72 const modelLoadRetryDelaysMs = [300, 900] const adjacentPreloadDelayMs = 900 const manualAutoSwitchPauseMs = 1500 +const autoSwitchExitDistanceRatio = 0.2 +const autoSwitchExitDistanceMarginRatio = 0.08 const poiMarkerVisualLiftFactor = 0.92 const poiAmbientLabelLiftFactor = 2.1 const poiFocusLabelLiftFactor = 2.72 @@ -3275,6 +3277,18 @@ const resetAutoSwitchDistanceTracking = () => { autoSwitchStateMachine.reset(activeView.value) } +const ensureFloorAutoExitZoomRange = () => { + if (!controls) return + + const distance = controls.getDistance() + if (!Number.isFinite(distance) || distance <= 0) return + + // 楼层退出以当前距离的 120% 为阈值;参考外观相机正好处于原 600 上限时, + // 必须预留额外缩小空间,否则切层后无法满足自动返回外观的条件。 + const requiredMaxDistance = distance * (1 + autoSwitchExitDistanceRatio + autoSwitchExitDistanceMarginRatio) + controls.maxDistance = Math.max(controls.maxDistance, requiredMaxDistance) +} + const handleWheelIntent = (event: WheelEvent) => { if (!controls || activeView.value !== 'overview' || event.deltaY === 0) return @@ -3368,6 +3382,7 @@ const autoSwitchStateMachine = new GuideAutoSwitchStateMachine({ exitHoldMs: 400, intentTimeoutMs: 2000, reverseToleranceRatio: 0.008, + exitRatio: autoSwitchExitDistanceRatio, cooldownMs: props.autoSwitchCooldown, canSwitch: canRunAutoSwitch, onSwitchRequested: requestAutoSwitch @@ -3752,6 +3767,7 @@ const applyIndoorInitialModelTransform = (model: THREE.Object3D) => { const resetCamera = () => { applyReferenceOverviewCameraState() if (activeView.value === 'floor' && controls) { + ensureFloorAutoExitZoomRange() autoSwitchStateMachine.setFloorInitialDistance(controls.getDistance()) } refreshPoiVisibilityByDistance() @@ -4060,6 +4076,7 @@ const commitPreparedFloorScene = ( } recordCameraSnapshotRestoration(cameraSnapshot, `floor:${expectedFloorId}`) if (controls) { + ensureFloorAutoExitZoomRange() autoSwitchStateMachine.setFloorInitialDistance(controls.getDistance()) } options.onCameraStable?.() diff --git a/tests/e2e/three-map-camera-stability.spec.ts b/tests/e2e/three-map-camera-stability.spec.ts index 0433047..69f49eb 100644 --- a/tests/e2e/three-map-camera-stability.spec.ts +++ b/tests/e2e/three-map-camera-stability.spec.ts @@ -168,6 +168,21 @@ const showOverview = async (page: Page, testInfo: TestInfo, name: string) => { assertStableTransition(before, after) } +const exitOverviewWithZoomControl = async (page: Page, testInfo: TestInfo, name: string) => { + const before = await getReport(page) + await screenshot(page, testInfo, `${name}-before`) + + await page.getByText('−', { exact: true }).click() + await expect.poll(async () => (await getReport(page)).activeView, { timeout: 10_000 }).toBe('overview') + + const after = await getReport(page) + await screenshot(page, testInfo, `${name}-after`) + expect(after.isCameraTweening).toBe(false) + expect(after.modelRoot, 'the overview model must stay committed').not.toBeNull() + expect(after.modelRoot!.maxError).toBeLessThan(1e-12) + expect(after.camera.distance).toBeGreaterThanOrEqual(before.camera.distance * 1.2) +} + const normalizeFloorKey = (value: string) => value.trim().toLowerCase().replace(/[^a-z0-9.-]/g, '') const resolveRequiredFloorId = (floors: FloorReport[], requested: string) => { @@ -213,4 +228,8 @@ test('ordinary exterior and floor switches preserve the GLB_METER camera project await switchFloor(page, testInfo, l1Point5, 'exterior-l1point5') await showOverview(page, testInfo, 'l1point5-exterior') + + await switchFloor(page, testInfo, l1, 'exterior-l1-auto-exit') + await switchFloor(page, testInfo, l2, 'l1-l2-auto-exit') + await exitOverviewWithZoomControl(page, testInfo, 'l2-exterior-auto-exit') })