15 KiB
馆内导览交互优化改进计划
文档信息
- 项目:深圳自然博物馆智能导览应用
- 模块:馆内 3D 导览交互
- 创建时间:2026-06-12
- 状态:待评审
背景
当前馆内导览采用显式"全馆/楼层"手动切换模式,用户需要点击按钮在"建筑外观"和"楼层内部"之间切换。这个模式功能完整,但与基于缩放距离的自动切换模式相比,交互直觉性较弱。
现有模式:
- 进入馆内 3D → 加载建筑外观
- 用户手动点击"楼层"按钮 → 楼层控件出现 → 选择楼层 → 加载楼层内部
- 用户缩放只改变视角距离,不触发模型切换
目标模式:
- 进入馆内 3D → 加载建筑外观
- 用户放大到一定距离 → 自动切换到楼层内部
- 用户缩小到一定距离 → 自动切换回建筑外观
- 保留手动控制入口作为兜底
改进目标
- 增加基于缩放距离的自动视角切换,提升交互直觉性。
- 保留手动切换入口,避免误触和性能抖动。
- 优化楼层控件可见性,缩短操作路径。
- 为未来多层展示预留架构空间(不在本次实施)。
改进任务分解
Phase 1:基础自动切换能力(核心)
目标:实现基于缩放距离的建筑外观 ↔ 楼层内部自动切换。
Task 1.1:在 ThreeMap 组件实现距离监听与自动切换逻辑
优先级:P0
预估工作量:2-3 天
风险:中等(需要调优阈值和滞回参数)
实现要点:
-
监听
OrbitControls的change事件,而不是每帧检查:controls.addEventListener('change', () => { throttledCheckAutoSwitch() }) -
实现双阈值滞回机制,避免反复切换:
const autoSwitchConfig = { thresholdLowFactor: 1.0, // 放大到建筑尺寸 1.0 倍时切换到楼层 thresholdHighFactor: 1.3, // 缩小到建筑尺寸 1.3 倍时切换回全馆 cooldownMs: 2000, // 切换后冷却 2 秒 enableAuto: true // 可配置开关 } -
增加加载锁和冷却时间:
let isAutoSwitchLocked = false let lastAutoSwitchTime = 0 const checkAutoSwitch = () => { if (!autoSwitchConfig.enableAuto) return if (isAutoSwitchLocked) return const now = Date.now() if (now - lastAutoSwitchTime < autoSwitchConfig.cooldownMs) return const distance = controls.getDistance() // ... 判断阈值并切换 } -
切换时触发事件通知上层:
emit('auto-switch', { from: 'overview', to: 'floor', trigger: 'zoom-in', distance: number })
验证标准:
- 在建筑外观缩放到近距离,自动切换到默认楼层(L1)。
- 在楼层内部缩放到远距离,自动切换回建筑外观。
- 快速缩放不会触发多次加载。
- 切换过程有 loading 状态提示。
文件修改:
src/components/map/ThreeMap.vue:增加自动切换逻辑src/domain/guideModel.ts:增加自动切换配置类型定义
Task 1.2:调整首页和 GuideMapShell 配合自动切换
优先级:P0
预估工作量:1 天
依赖:Task 1.1
实现要点:
-
监听
ThreeMap的auto-switch事件,同步indoorView状态:const handleAutoSwitch = (event: { from: string; to: string }) => { if (event.to === 'floor') { indoorView.value = 'floor' } else { indoorView.value = 'overview' } } -
更新状态标签文案:
- 自动切换时显示"馆内楼层(自动)"或"馆内全馆(自动)"
- 手动切换时显示"馆内楼层"或"馆内全馆"
-
楼层控件可见性调整:
- 改为
:show-floor="is3DMode",在全馆模式也显示楼层控件 - 全馆模式下点击楼层,先切换到楼层视图,再加载对应楼层
- 改为
验证标准:
- 自动切换后,顶部状态标签正确更新。
- 全馆模式下可以直接点击楼层控件选择楼层。
文件修改:
src/pages/index/index.vue:监听自动切换事件src/components/navigation/GuideMapShell.vue:调整楼层控件可见性
Task 1.3:保留"全馆/楼层"手动切换作为兜底
优先级:P1
预估工作量:0.5 天
依赖:Task 1.1, 1.2
实现要点:
-
保留现有"全馆/楼层"切换按钮,但调整定位:
- 不再是主交互入口,而是手动控制和兜底入口。
- 样式可以调整为更轻量(例如图标按钮,而不是双按钮切换组)。
-
手动切换时临时禁用自动切换(例如 10 秒):
const handleManualSwitch = (view: 'overview' | 'floor') => { // 临时禁用自动切换 threeMapRef.value?.disableAutoSwitchTemporarily?.(10000) // 执行手动切换 if (view === 'overview') { void threeMapRef.value?.showOverview?.() } else { void threeMapRef.value?.switchFloor?.(activeFloorId.value) } indoorView.value = view } -
增加设置入口,允许用户永久禁用自动切换(可选)。
验证标准:
- 手动切换后,10 秒内不会因缩放触发自动切换。
- 手动切换按钮始终可用。
文件修改:
src/components/map/ThreeMap.vue:增加临时禁用自动切换方法src/components/navigation/GuideMapShell.vue:调整切换按钮样式和行为
Phase 2:交互细节优化(增强)
目标:优化自动切换的用户体验细节。
Task 2.1:增加视觉过渡和状态提示
优先级:P1
预估工作量:1 天
依赖:Task 1.1
实现要点:
-
自动切换时增加淡入淡出过渡:
const transitionSwitch = async (loadFn: () => Promise<void>) => { // 淡出当前模型 if (activeModel) { await fadeOut(activeModel, 300) } // 加载新模型 await loadFn() // 淡入新模型 if (activeModel) { await fadeIn(activeModel, 300) } } -
切换时显示提示文案:
- "正在切换到楼层视图..."
- "正在切换到完整建筑..."
-
首次自动切换时显示引导提示(Toast):
- "已自动切换到楼层内部,您也可以手动控制视图"
- 提示只显示一次,存储到本地缓存
验证标准:
- 切换过程视觉流畅,无突兀感。
- 首次自动切换有引导提示。
文件修改:
src/components/map/ThreeMap.vue:增加过渡动画src/pages/index/index.vue:增加首次引导提示
Task 2.2:优化阈值和参数
优先级:P2
预估工作量:1-2 天(需要实测调优)
依赖:Task 1.1
实现要点:
-
在真实设备(手机、平板)上测试不同阈值的效果:
- 建筑尺寸的 0.8x / 1.0x / 1.2x
- 冷却时间 1s / 2s / 3s
-
针对不同建筑尺寸动态调整阈值:
const calculateThresholds = (modelSize: THREE.Vector3) => { const maxDim = Math.max(modelSize.x, modelSize.y, modelSize.z, 1) // 大建筑用较大倍数,小建筑用较小倍数 const sizeFactor = maxDim > 100 ? 1.2 : 1.0 return { low: maxDim * sizeFactor * 0.9, high: maxDim * sizeFactor * 1.1 } } -
记录切换频率和用户行为,优化参数。
验证标准:
- 移动端测试无误触。
- 切换时机符合用户预期。
- 不会频繁切换导致加载抖动。
文件修改:
src/components/map/ThreeMap.vue:优化阈值计算
Phase 3:多层展示支持(未来)
目标:支持单层/多层显示模式切换,适配跨层路线展示。
前置条件:
route_graph和nav_data已接入- 跨层路线规划能力已实现
Task 3.1:扩展 ViewMode 类型和多层加载逻辑
优先级:P3(依赖路线能力)
预估工作量:3-4 天
实现要点:
-
扩展
ViewMode类型:type ViewMode = 'overview' | 'single-floor' | 'multi-floor' -
实现
loadMultiFloor方法:const loadMultiFloor = async (floorIds: string[]) => { activeView.value = 'multi-floor' clearSceneData() const verticalSpacing = 10 // 楼层间距 for (const [index, floorId] of floorIds.entries()) { const floor = floorIndex.value.find(f => f.floorId === floorId) if (!floor) continue const gltf = await loadModel(floorModelUrl(floor), `加载 ${floor.label}`) const model = gltf.scene model.position.y = index * verticalSpacing prepareModel(model) scene.add(model) } fitCameraToMultiFloor(floorIds) } -
注意性能优化:
- 多层模型总面数可能很大,需要监控帧率
- 考虑 LOD(细节层次)或简化模型
验证标准:
- 可以同时加载并显示多个楼层。
- 楼层保持合理的垂直间距。
- 帧率在可接受范围(>30fps)。
文件修改:
src/components/map/ThreeMap.vue:增加多层加载逻辑src/domain/guideModel.ts:扩展 ViewMode 类型
Task 3.2:增加"单层/多层"切换控件
优先级:P3
预估工作量:1 天
依赖:Task 3.1
实现要点:
-
在
GuideMapShell增加切换控件:<view v-if="mapType === 'indoor' && indoorView === 'floor'" class="floor-display-mode"> <view class="mode-item" :class="{ active: floorDisplayMode === 'single' }"> <text>单层</text> </view> <view class="mode-item" :class="{ active: floorDisplayMode === 'multi' }"> <text>多层</text> </view> </view> -
多层模式下显示楼层选择器,支持多选:
- 用户可以勾选要同时显示的楼层(例如 B2、B1、1F)
- 默认显示当前楼层的上下各一层
验证标准:
- 切换到多层模式,可以选择要显示的楼层。
- 多层模式下可以看到跨层路线完整路径。
文件修改:
src/components/navigation/GuideMapShell.vue:增加多层切换控件src/pages/index/index.vue:增加多层模式状态管理
实施优先级
必须实施(Phase 1)
这些任务实现核心的自动切换能力,显著提升交互直觉性。
- Task 1.1:实现距离监听与自动切换逻辑
- Task 1.2:调整首页和 GuideMapShell 配合自动切换
- Task 1.3:保留手动切换作为兜底
预估总工作量:3.5-4.5 天
建议实施(Phase 2)
这些任务优化用户体验细节,提升产品完成度。
- Task 2.1:增加视觉过渡和状态提示
- Task 2.2:优化阈值和参数
预估总工作量:2-3 天
未来实施(Phase 3)
这些任务依赖跨层路线能力,优先级较低。
- Task 3.1:扩展 ViewMode 和多层加载逻辑
- Task 3.2:增加"单层/多层"切换控件
预估总工作量:4-5 天
前置条件:route_graph 和 nav_data 接入完成
技术风险与应对
风险 1:自动切换触发频繁,导致加载抖动
影响:用户体验差,可能导致卡顿。
应对措施:
- 使用双阈值滞回机制(低阈值 1.0x,高阈值 1.3x)
- 增加冷却时间(2 秒内不重复触发)
- 增加加载锁(切换期间锁定,不响应新的切换请求)
- 监听
controls.change事件而不是每帧检查,减少计算频率
风险 2:移动端误触缩放,导致意外切换
影响:用户可能在查看建筑外观时误触放大,意外切换到楼层。
应对措施:
- 手动切换后临时禁用自动切换(10 秒)
- 提供设置项,允许用户永久禁用自动切换
- 优化阈值,避免轻微缩放就触发
- 首次自动切换时显示引导提示,告知用户可以手动控制
风险 3:多层模式性能问题
影响:同时加载多个楼层可能导致内存占用过高、帧率下降。
应对措施:
- 限制最多同时显示 3-4 层
- 考虑使用 LOD(细节层次),远处楼层使用简化模型
- 监控帧率,低于阈值时提示用户减少显示楼层
- 多层模式下禁用部分高级渲染特性(例如阴影)
质量保证
单元测试
ThreeMap.vue的距离计算和阈值判断逻辑- 自动切换状态机测试(overview → floor → overview)
- 加载锁和冷却时间逻辑测试
集成测试
- 完整的用户流程:进入馆内 → 缩放 → 自动切换 → 手动切换 → 楼层切换
- 自动切换与手动切换的交互测试
- 多层模式与单层模式切换测试
移动端测试
- 在真实移动设备(iOS/Android)测试缩放手势
- 测试误触场景
- 测试弱网环境下的加载体验
性能测试
- 监控切换过程的内存占用
- 监控多层模式的帧率
- 测试大建筑和小建筑的阈值适配性
验收标准
Phase 1 验收标准
- 用户在建筑外观放大到一定距离,自动切换到默认楼层(L1)
- 用户在楼层内部缩小到一定距离,自动切换回建筑外观
- 快速缩放不会触发多次加载
- 手动切换后 10 秒内不会自动切换
- 全馆模式下可以直接点击楼层控件选择楼层
- 移动端测试无明显误触
Phase 2 验收标准
- 切换过程有视觉过渡,无突兀感
- 首次自动切换有引导提示
- 切换时有 loading 状态提示
- 阈值在真实设备上测试符合预期
Phase 3 验收标准
- 可以切换到多层模式,同时显示多个楼层
- 多层模式帧率 >30fps
- 多层模式下可以看到跨层路线完整路径
附录:配置项设计
ThreeMap 组件新增 Props
interface ThreeMapProps {
// ... 现有 props
// 自动切换配置
autoSwitch?: boolean // 是否启用自动切换,默认 true
autoSwitchThresholdLow?: number // 下阈值倍数,默认 1.0
autoSwitchThresholdHigh?: number // 上阈值倍数,默认 1.3
autoSwitchCooldown?: number // 冷却时间(毫秒),默认 2000
// 多层模式配置(Phase 3)
enableMultiFloor?: boolean // 是否启用多层模式,默认 false
multiFloorVerticalSpacing?: number // 楼层间距(米),默认 10
}
新增 Events
// ThreeMap emit 的新事件
emit('auto-switch', {
from: 'overview' | 'floor',
to: 'overview' | 'floor',
trigger: 'zoom-in' | 'zoom-out',
distance: number
})
emit('auto-switch-blocked', {
reason: 'loading-locked' | 'cooldown' | 'disabled'
})
参考资料
- OrbitControls 文档:https://threejs.org/docs/#examples/en/controls/OrbitControls
- Three.js 性能优化最佳实践:https://threejs.org/manual/#en/optimize-lots-of-objects
- 馆内导览产品参考:机场导览 App、商场导览 App、博物馆 3D 导览
变更记录
| 日期 | 版本 | 变更内容 | 作者 |
|---|---|---|---|
| 2026-06-12 | v1.0 | 初始版本 | Claude |