@realsee/five
Version:
Realsee Renderer SDK
182 lines (135 loc) • 6.39 kB
Markdown
- **Summary**: Five 提供了一套跨平台的手势事件系统,抹平了 PC 与移动端的差异,并针对不同 Mode 提供了默认的交互逻辑。
- **Schema**: `GestureEvent` 继承自 `StateEvent`,包含了手势中心点、射线等信息。
- **Concepts**: Gesture Type, Default Behavior, PreventDefault。
- **Examples**: 阻止默认行为、点击添加标记。
> **Definition**: [GestureEvent](../../five/utils/event.d.ts)
所有手势事件的回调参数均为 `GestureEvent` 类型:
```typescript
import { State } from "@realsee/five";
import * as THREE from "three";
interface GestureEvent {
type: string; // 事件类型,如 "gesture.tap"
timeStamp: number; // 时间戳
target: EventTarget | null;
pointerType: 'touch' | 'pen' | 'mouse' | 'kinect';
// 手势中心点信息
center: {
x: number; // 屏幕 X 坐标
y: number; // 屏幕 Y 坐标
raycaster: THREE.Raycaster; // 从相机发出的射线(已包含坐标变换)
};
// 当前相机状态
state: State;
// 是否是用户触发(区别于代码调用)
userAction: boolean;
// 阻止默认行为
preventDefault(): void;
defaultPrevented: boolean;
}
```
Five 的手势系统基于 Hammer.js 封装,主要包含以下几种手势:
* **gesture.fire**: 任意事件被触发。
* **gesture.pan**: 拖拽/平移。
* **gesture.pinch**: 双指缩放。
* **gesture.tap**: 点击。
* **gesture.dbltap**: 双击。
* **gesture.press**: 长按。
* **gesture.mousewheel**: 鼠标滚轮。
不同的 Mode 对手势有不同的**默认响应**:
| 手势 | Panorama (全景) | Mapview (模型) | 说明 |
| :--- | :--- | :--- | :--- |
| **Pan** | 旋转相机视角 (Look around) | 围绕模型旋转/平移相机 (Orbit/Pan) | |
| **Pinch** | 调整 FOV (Zoom in/out) | 调整相机距离 (Distance) | |
| **Tap** | **移动点位** (Move to pano) | **切换到全景** (Enter pano) | 若点击到地面/模型,Mapview 下默认无响应,除非点击到点位标记 |
| **DblTap** | 动画调整 FOV | 聚焦并拉近视角 (Fly to) | |
| **MouseWheel** | 调整 FOV | 调整相机距离 | |
Five 的事件触发顺序如下:
1. **Emit Event**: Five 触发 `gesture.xxx` 事件。
2. **User Callback**: 开发者注册的监听函数被执行。
3. **Default Behavior**: **如果**开发者没有调用 `event.preventDefault()`,Five 执行默认逻辑(如旋转相机、移动点位)。
因此,如果你想自定义交互(例如:点击不走点,而是弹出一个框),**必须**调用 `event.preventDefault()`。
监听点击事件并打印日志:
```typescript
five.on("gesture.tap", (event) => {
console.log("点击位置:", event.center.x, event.center.y);
// event.center.raycaster 可以用来做射线检测
});
```
这个示例展示了如何通过点击事件在场景中添加一个红色的球体,并阻止 Five 默认的点位移动行为。
```typescript
import * as THREE from "three";
import { Five } from "@realsee/five";
const five = new Five();
five.on("gesture.tap", (event) => {
// 1. 阻止默认行为(防止点击地面时 Five 自动走点或切换模式)
event.preventDefault();
// 2. 获取射线
const raycaster = event.center.raycaster;
// 3. 检测与模型的碰撞
// five.model.intersectRaycaster 是 Five 提供的便捷方法,会自动检测所有可见模型
const intersects = five.model.intersectRaycaster(raycaster);
if (intersects.length > 0) {
const intersect = intersects[0];
const point = intersect.point; // 碰撞点世界坐标
console.log("点击到了模型,坐标:", point);
// 4. 创建一个小球
const geometry = new THREE.SphereGeometry(0.1, 32, 32);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const sphere = new THREE.Mesh(geometry, material);
// 5. 设置位置并添加到场景
sphere.position.copy(point);
five.scene.add(sphere);
// 强制刷新渲染,确保小球立即出现
five.needsRender = true;
} else {
console.log("未点击到任何模型");
}
});
```
```typescript
five.on("gesture.pinch", (event) => {
// 获取当前 FOV
const currentFov = five.state.fov;
// 假设我们限制 FOV 在 80 到 100 之间
// 注意:这只是逻辑演示,Five 内部有自己的 minFov/maxFov 配置,
// 这里演示如何完全接管控制权。
if (currentFov < 80 || currentFov > 100) {
// 阻止 Five 的默认缩放
event.preventDefault();
console.log("已达到缩放极限");
}
});
```
* **查看事件触发**: 在控制台监听所有手势。
```typescript
["pan", "tap", "pinch", "press"].forEach(type => {
five.on(`gesture.${type}` as any, (e) => console.log(type, e));
});
```
* **Helper Line**: 如果射线检测不准,可以画出射线辅助线。
```typescript
five.scene.add(new THREE.ArrowHelper(raycaster.ray.direction, raycaster.ray.origin, 10, 0xff0000));
five.needsRender = true;
```
1. **忘记 preventDefault**: 如果你发现点击后既生成了小球,相机又自动飞走了,通常是因为没有调用 `event.preventDefault()`。
2. **坐标系混淆**: `event.center.x/y` 是屏幕像素坐标,`event.center.raycaster` 已经处理了屏幕到世界的转换。直接使用 `raycaster` 最安全。
3. **点击穿透**: 如果场景中有 HTML 覆盖层(如按钮),HTML 元素的点击事件可能会冒泡或被 Five 捕获。建议在 HTML 元素上使用 `stopPropagation` 或判断 `event.target`。
* [Mode (模式)](./mode.md): 了解不同模式下的默认行为。
* [State (状态)](./state.md): 手势最终改变的是 State。
* [Raycast (射线检测)](./raycast.md): 深入了解 `intersectRaycaster` 的工作原理。
---
```yaml
tags: [手势, 点击, 拖拽, 缩放, 长按, 鼠标, 触摸, 交互控制, 自定义交互, gesture, event, interaction, tap, pan, pinch, preventDefault]
```