feat: refine guide explain UX and QA docs

This commit is contained in:
lyf
2026-06-12 17:20:39 +08:00
parent 2055b13b90
commit a7c1879f60
27 changed files with 2831 additions and 1362 deletions

View File

@@ -0,0 +1,544 @@
# 室内导览交互优化改进计划
## 文档信息
- **项目**:深圳自然博物馆智能导览应用
- **模块**:室内 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 |