545 lines
15 KiB
Markdown
545 lines
15 KiB
Markdown
# 馆内导览交互优化改进计划
|
||
|
||
## 文档信息
|
||
|
||
- **项目**:深圳自然博物馆智能导览应用
|
||
- **模块**:馆内 3D 导览交互
|
||
- **创建时间**:2026-06-12
|
||
- **状态**:待评审
|
||
|
||
---
|
||
|
||
## 背景
|
||
|
||
当前馆内导览采用**显式"全馆/楼层"手动切换**模式,用户需要点击按钮在"建筑外观"和"楼层内部"之间切换。这个模式功能完整,但与基于缩放距离的自动切换模式相比,交互直觉性较弱。
|
||
|
||
**现有模式**:
|
||
- 进入馆内 3D → 加载建筑外观
|
||
- 用户手动点击"楼层"按钮 → 楼层控件出现 → 选择楼层 → 加载楼层内部
|
||
- 用户缩放只改变视角距离,不触发模型切换
|
||
|
||
**目标模式**:
|
||
- 进入馆内 3D → 加载建筑外观
|
||
- 用户放大到一定距离 → **自动切换**到楼层内部
|
||
- 用户缩小到一定距离 → **自动切换**回建筑外观
|
||
- 保留手动控制入口作为兜底
|
||
|
||
---
|
||
|
||
## 改进目标
|
||
|
||
1. **增加基于缩放距离的自动视角切换**,提升交互直觉性。
|
||
2. **保留手动切换入口**,避免误触和性能抖动。
|
||
3. **优化楼层控件可见性**,缩短操作路径。
|
||
4. **为未来多层展示预留架构空间**(不在本次实施)。
|
||
|
||
---
|
||
|
||
## 改进任务分解
|
||
|
||
### Phase 1:基础自动切换能力(核心)
|
||
|
||
**目标**:实现基于缩放距离的建筑外观 ↔ 楼层内部自动切换。
|
||
|
||
#### Task 1.1:在 ThreeMap 组件实现距离监听与自动切换逻辑
|
||
|
||
**优先级**:P0
|
||
**预估工作量**:2-3 天
|
||
**风险**:中等(需要调优阈值和滞回参数)
|
||
|
||
**实现要点**:
|
||
|
||
1. 监听 `OrbitControls` 的 `change` 事件,而不是每帧检查:
|
||
```typescript
|
||
controls.addEventListener('change', () => {
|
||
throttledCheckAutoSwitch()
|
||
})
|
||
```
|
||
|
||
2. 实现双阈值滞回机制,避免反复切换:
|
||
```typescript
|
||
const autoSwitchConfig = {
|
||
thresholdLowFactor: 1.0, // 放大到建筑尺寸 1.0 倍时切换到楼层
|
||
thresholdHighFactor: 1.3, // 缩小到建筑尺寸 1.3 倍时切换回全馆
|
||
cooldownMs: 2000, // 切换后冷却 2 秒
|
||
enableAuto: true // 可配置开关
|
||
}
|
||
```
|
||
|
||
3. 增加加载锁和冷却时间:
|
||
```typescript
|
||
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()
|
||
// ... 判断阈值并切换
|
||
}
|
||
```
|
||
|
||
4. 切换时触发事件通知上层:
|
||
```typescript
|
||
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
|
||
|
||
**实现要点**:
|
||
|
||
1. 监听 `ThreeMap` 的 `auto-switch` 事件,同步 `indoorView` 状态:
|
||
```typescript
|
||
const handleAutoSwitch = (event: { from: string; to: string }) => {
|
||
if (event.to === 'floor') {
|
||
indoorView.value = 'floor'
|
||
} else {
|
||
indoorView.value = 'overview'
|
||
}
|
||
}
|
||
```
|
||
|
||
2. 更新状态标签文案:
|
||
- 自动切换时显示"馆内楼层(自动)"或"馆内全馆(自动)"
|
||
- 手动切换时显示"馆内楼层"或"馆内全馆"
|
||
|
||
3. 楼层控件可见性调整:
|
||
- 改为 `:show-floor="is3DMode"`,在全馆模式也显示楼层控件
|
||
- 全馆模式下点击楼层,先切换到楼层视图,再加载对应楼层
|
||
|
||
**验证标准**:
|
||
- 自动切换后,顶部状态标签正确更新。
|
||
- 全馆模式下可以直接点击楼层控件选择楼层。
|
||
|
||
**文件修改**:
|
||
- `src/pages/index/index.vue`:监听自动切换事件
|
||
- `src/components/navigation/GuideMapShell.vue`:调整楼层控件可见性
|
||
|
||
---
|
||
|
||
#### Task 1.3:保留"全馆/楼层"手动切换作为兜底
|
||
|
||
**优先级**:P1
|
||
**预估工作量**:0.5 天
|
||
**依赖**:Task 1.1, 1.2
|
||
|
||
**实现要点**:
|
||
|
||
1. 保留现有"全馆/楼层"切换按钮,但调整定位:
|
||
- 不再是主交互入口,而是手动控制和兜底入口。
|
||
- 样式可以调整为更轻量(例如图标按钮,而不是双按钮切换组)。
|
||
|
||
2. 手动切换时临时禁用自动切换(例如 10 秒):
|
||
```typescript
|
||
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
|
||
}
|
||
```
|
||
|
||
3. 增加设置入口,允许用户永久禁用自动切换(可选)。
|
||
|
||
**验证标准**:
|
||
- 手动切换后,10 秒内不会因缩放触发自动切换。
|
||
- 手动切换按钮始终可用。
|
||
|
||
**文件修改**:
|
||
- `src/components/map/ThreeMap.vue`:增加临时禁用自动切换方法
|
||
- `src/components/navigation/GuideMapShell.vue`:调整切换按钮样式和行为
|
||
|
||
---
|
||
|
||
### Phase 2:交互细节优化(增强)
|
||
|
||
**目标**:优化自动切换的用户体验细节。
|
||
|
||
#### Task 2.1:增加视觉过渡和状态提示
|
||
|
||
**优先级**:P1
|
||
**预估工作量**:1 天
|
||
**依赖**:Task 1.1
|
||
|
||
**实现要点**:
|
||
|
||
1. 自动切换时增加淡入淡出过渡:
|
||
```typescript
|
||
const transitionSwitch = async (loadFn: () => Promise<void>) => {
|
||
// 淡出当前模型
|
||
if (activeModel) {
|
||
await fadeOut(activeModel, 300)
|
||
}
|
||
|
||
// 加载新模型
|
||
await loadFn()
|
||
|
||
// 淡入新模型
|
||
if (activeModel) {
|
||
await fadeIn(activeModel, 300)
|
||
}
|
||
}
|
||
```
|
||
|
||
2. 切换时显示提示文案:
|
||
- "正在切换到楼层视图..."
|
||
- "正在切换到完整建筑..."
|
||
|
||
3. 首次自动切换时显示引导提示(Toast):
|
||
- "已自动切换到楼层内部,您也可以手动控制视图"
|
||
- 提示只显示一次,存储到本地缓存
|
||
|
||
**验证标准**:
|
||
- 切换过程视觉流畅,无突兀感。
|
||
- 首次自动切换有引导提示。
|
||
|
||
**文件修改**:
|
||
- `src/components/map/ThreeMap.vue`:增加过渡动画
|
||
- `src/pages/index/index.vue`:增加首次引导提示
|
||
|
||
---
|
||
|
||
#### Task 2.2:优化阈值和参数
|
||
|
||
**优先级**:P2
|
||
**预估工作量**:1-2 天(需要实测调优)
|
||
**依赖**:Task 1.1
|
||
|
||
**实现要点**:
|
||
|
||
1. 在真实设备(手机、平板)上测试不同阈值的效果:
|
||
- 建筑尺寸的 0.8x / 1.0x / 1.2x
|
||
- 冷却时间 1s / 2s / 3s
|
||
|
||
2. 针对不同建筑尺寸动态调整阈值:
|
||
```typescript
|
||
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
|
||
}
|
||
}
|
||
```
|
||
|
||
3. 记录切换频率和用户行为,优化参数。
|
||
|
||
**验证标准**:
|
||
- 移动端测试无误触。
|
||
- 切换时机符合用户预期。
|
||
- 不会频繁切换导致加载抖动。
|
||
|
||
**文件修改**:
|
||
- `src/components/map/ThreeMap.vue`:优化阈值计算
|
||
|
||
---
|
||
|
||
### Phase 3:多层展示支持(未来)
|
||
|
||
**目标**:支持单层/多层显示模式切换,适配跨层路线展示。
|
||
|
||
**前置条件**:
|
||
- `route_graph` 和 `nav_data` 已接入
|
||
- 跨层路线规划能力已实现
|
||
|
||
#### Task 3.1:扩展 ViewMode 类型和多层加载逻辑
|
||
|
||
**优先级**:P3(依赖路线能力)
|
||
**预估工作量**:3-4 天
|
||
|
||
**实现要点**:
|
||
|
||
1. 扩展 `ViewMode` 类型:
|
||
```typescript
|
||
type ViewMode = 'overview' | 'single-floor' | 'multi-floor'
|
||
```
|
||
|
||
2. 实现 `loadMultiFloor` 方法:
|
||
```typescript
|
||
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)
|
||
}
|
||
```
|
||
|
||
3. 注意性能优化:
|
||
- 多层模型总面数可能很大,需要监控帧率
|
||
- 考虑 LOD(细节层次)或简化模型
|
||
|
||
**验证标准**:
|
||
- 可以同时加载并显示多个楼层。
|
||
- 楼层保持合理的垂直间距。
|
||
- 帧率在可接受范围(>30fps)。
|
||
|
||
**文件修改**:
|
||
- `src/components/map/ThreeMap.vue`:增加多层加载逻辑
|
||
- `src/domain/guideModel.ts`:扩展 ViewMode 类型
|
||
|
||
---
|
||
|
||
#### Task 3.2:增加"单层/多层"切换控件
|
||
|
||
**优先级**:P3
|
||
**预估工作量**:1 天
|
||
**依赖**:Task 3.1
|
||
|
||
**实现要点**:
|
||
|
||
1. 在 `GuideMapShell` 增加切换控件:
|
||
```vue
|
||
<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>
|
||
```
|
||
|
||
2. 多层模式下显示楼层选择器,支持多选:
|
||
- 用户可以勾选要同时显示的楼层(例如 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
|
||
|
||
```typescript
|
||
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
|
||
|
||
```typescript
|
||
// 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 |
|