This repository has been archived by the owner on Jun 21, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathinteractive-object.js
129 lines (121 loc) · 3.74 KB
/
interactive-object.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { Object3D, Raycaster, Vector2, PerspectiveCamera } from 'three';
import EventEmitter from 'eventemitter3';
import TouchControls from './touch-controls';
import type pointersArray from './touch-controls';
import renderer from '../rendering/renderer';
/**
* Adds mouse and touch events to Object3D inherited objects
*
* @export
* @class InteractiveObject
* @extends {EventEmitter}
*/
export default class InteractiveObject extends EventEmitter {
constructor(object: Object3D, camera: PerspectiveCamera, options: Object = {}) {
super();
this.object = object;
this.camera = camera;
this.options = Object.assign(
{
mouseMove: false, // raycast everytime the mouse moves
touchStart: true, // only fires when clicking down on an object successfully
touchMove: true, // fires when mouse or touch is moved on and off an object
touchEnd: true // fires when touch or mouse is released on and off an object
},
options
);
this.touchControls = new TouchControls(renderer.domElement, { hover: true });
this.raycaster = new Raycaster();
this.coords = new Vector2();
this.intersects = null;
this.fired = {
hoverOut: true, // Only fire hover out once per rollover
hoverOver: false // Only fire hover out once per rollover
};
this.bindEvents(true);
}
/**
* Bind mouse and touch events
*
* @memberof InteractiveObject
*/
bindEvents = (bind: boolean) => {
const listener = bind ? 'on' : 'off';
if (this.options.touchStart) this.touchControls[listener]('start', this.onTouchStart);
if (this.options.touchMove) this.touchControls[listener]('move', this.onTouchMove);
if (this.options.touchMove) this.touchControls[listener]('mousemove', this.onTouchMove);
if (this.options.touchEnd || this.options.touchMove) this.touchControls[listener]('end', this.onTouchEnd);
};
/**
* Touch start handler
*
* @memberof InteractiveObject
*/
onTouchStart = (event: pointersArray[]) => {
this.setCoords(event[0].normalX, event[0].normalY);
this.intersected = this.raycast();
if (this.intersected) this.emit('start', this.intersects[0]);
};
/**
* Touch and mouse move handler
*
* @memberof InteractiveObject
*/
onTouchMove = (event: pointersArray[]) => {
this.setCoords(event[0].normalX, event[0].normalY);
this.intersected = this.raycast();
this.hovering = this.intersected;
if (this.intersected) {
if (!this.fired.hoverOver || this.options.mouseMove) this.emit('hover', true, this.intersects[0]);
this.fired.hoverOut = false;
this.fired.hoverOver = true;
} else if (!this.fired.hoverOut) {
this.fired.hoverOut = true;
this.fired.hoverOver = false;
this.emit('hover', false);
}
};
/**
* Touch and hover out handler
*
* @memberof InteractiveObject
*/
onTouchEnd = (event: pointersArray[]) => {
if (this.hovering) {
this.hovering = false;
this.emit('hover', false);
}
if (this.intersected) {
this.intersected = false;
this.emit('end');
}
};
/**
* Set the screenspace coords for the raycaster
*
* @memberof InteractiveObject
*/
setCoords = (normalX: number, normalY: number) => {
this.coords.x = normalX * 2 - 1;
this.coords.y = -normalY * 2 + 1;
};
/**
* Raycast against the object
*
* @memberof InteractiveObject
*/
raycast = (): boolean => {
this.raycaster.setFromCamera(this.coords, this.camera);
this.intersects = this.raycaster.intersectObject(this.object);
return this.intersects.length > 0;
};
/**
* Dispose and unbind events
*
* @memberof InteractiveObject
*/
dispose = () => {
this.touchControls.dispose();
this.bindEvents(false);
};
}