PageSourceSearch

https://alexgrosek.com/three/FlyControls.js

js alexgrosek.com collected 2026-10-08 21:36:59 UTC 7,091 bytes, 284 lines download raw bytes

1import {
2	EventDispatcher,
3	Quaternion,
4	Vector3
5} from '../three/three.module.js';
6
7const _changeEvent = { type: 'change' };
8
9class FlyControls extends EventDispatcher {
10
11	constructor( object, domElement ) {
12
13		super();
14
15		this.object = object;
16		this.domElement = domElement;
17
18		// API
19
20		this.movementSpeed = 1.0;
21		this.rollSpeed = 0.005;
22
23		this.dragToLook = false;
24		this.autoForward = false;
25
26		// disable default target object behavior
27
28		// internals
29
30		const scope = this;
31
32		const EPS = 0.000001;
33
34		const lastQuaternion = new Quaternion();
35		const lastPosition = new Vector3();
36
37		this.tmpQuaternion = new Quaternion();
38
39		this.status = 0;
40
41		this.moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 };
42		this.moveVector = new Vector3( 0, 0, 0 );
43		this.rotationVector = new Vector3( 0, 0, 0 );
44
45		this.keydown = function ( event ) {
46
47			if ( event.altKey ) {
48
49				return;
50
51			}
52
53			switch ( event.code ) {
54
55				case 'ShiftLeft':
56				case 'ShiftRight': this.movementSpeedMultiplier = .1; break;
57
58				case 'KeyW': this.moveState.forward = 1; break;
59				case 'KeyS': this.moveState.back = 1; break;
60
61				case 'KeyA': this.moveState.left = 1; break;
62				case 'KeyD': this.moveState.right = 1; break;
63
64				case 'KeyR': this.moveState.up = 1; break;
65				case 'KeyF': this.moveState.down = 1; break;
66
67				case 'ArrowUp': this.moveState.pitchUp = 1; break;
68				case 'ArrowDown': this.moveState.pitchDown = 1; break;
69
70				case 'ArrowLeft': this.moveState.yawLeft = 1; break;
71				case 'ArrowRight': this.moveState.yawRight = 1; break;
72
73				case 'KeyQ': this.moveState.rollLeft = 1; break;
74				case 'KeyE': this.moveState.rollRight = 1; break;
75
76			}
77
78			this.updateMovementVector();
79			this.updateRotationVector();
80
81		};
82
83		this.keyup = function ( event ) {
84
85			switch ( event.code ) {
86
87				case 'ShiftLeft':
88				case 'ShiftRight': this.movementSpeedMultiplier = 1; break;
89
90				case 'KeyW': this.moveState.forward = 0; break;
91				case 'KeyS': this.moveState.back = 0; break;
92
93				case 'KeyA': this.moveState.left = 0; break;
94				case 'KeyD': this.moveState.right = 0; break;
95
96				case 'KeyR': this.moveState.up = 0; break;
97				case 'KeyF': this.moveState.down = 0; break;
98
99				case 'ArrowUp': this.moveState.pitchUp = 0; break;
100				case 'ArrowDown': this.moveState.pitchDown = 0; break;
101
102				case 'ArrowLeft': this.moveState.yawLeft = 0; break;
103				case 'ArrowRight': this.moveState.yawRight = 0; break;
104
105				case 'KeyQ': this.moveState.rollLeft = 0; break;
106				case 'KeyE': this.moveState.rollRight = 0; break;
107
108			}
109
110			this.updateMovementVector();
111			this.updateRotationVector();
112
113		};
114
115		this.pointerdown = function ( event ) {
116
117			if ( this.dragToLook ) {
118
119				this.status ++;
120
121			} else {
122
123				switch ( event.button ) {
124
125					case 0: this.moveState.forward = 1; break;
126					case 2: this.moveState.back = 1; break;
127
128				}
129
130				this.updateMovementVector();
131
132			}
133
134		};
135
136		this.pointermove = function ( event ) {
137
138			if ( ! this.dragToLook || this.status > 0 ) {
139
140				const container = this.getContainerDimensions();
141				const halfWidth = container.size[ 0 ] / 2;
142				const halfHeight = container.size[ 1 ] / 2;
143
144				this.moveState.yawLeft = - ( ( event.pageX - container.offset[ 0 ] ) - halfWidth ) / halfWidth;
145				this.moveState.pitchDown = ( ( event.pageY - container.offset[ 1 ] ) - halfHeight ) / halfHeight;
146
147				this.updateRotationVector();
148
149			}
150
151		};
152
153		this.pointerup = function ( event ) {
154
155			if ( this.dragToLook ) {
156
157				this.status --;
158
159				this.moveState.yawLeft = this.moveState.pitchDown = 0;
160
161			} else {
162
163				switch ( event.button ) {
164
165					case 0: this.moveState.forward = 0; break;
166					case 2: this.moveState.back = 0; break;
167
168				}
169
170				this.updateMovementVector();
171
172			}
173
174			this.updateRotationVector();
175
176		};
177
178		this.update = function ( delta ) {
179
180			const moveMult = delta * scope.movementSpeed;
181			const rotMult = delta * scope.rollSpeed;
182
183			scope.object.translateX( scope.moveVector.x * moveMult );
184			scope.object.translateY( scope.moveVector.y * moveMult );
185			scope.object.translateZ( scope.moveVector.z * moveMult );
186
187			scope.tmpQuaternion.set( scope.rotationVector.x * rotMult, scope.rotationVector.y * rotMult, scope.rotationVector.z * rotMult, 1 ).normalize();
188			scope.object.quaternion.multiply( scope.tmpQuaternion );
189
190			if (
191				lastPosition.distanceToSquared( scope.object.position ) > EPS ||
192				8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS
193			) {
194
195				scope.dispatchEvent( _changeEvent );
196				lastQuaternion.copy( scope.object.quaternion );
197				lastPosition.copy( scope.object.position );
198
199			}
200
201		};
202
203		this.updateMovementVector = function () {
204
205			const forward = ( this.moveState.forward || ( this.autoForward && ! this.moveState.back ) ) ? 1 : 0;
206
207			this.moveVector.x = ( - this.moveState.left + this.moveState.right );
208			this.moveVector.y = ( - this.moveState.down + this.moveState.up );
209			this.moveVector.z = ( - forward + this.moveState.back );
210
211			//console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] );
212
213		};
214
215		this.updateRotationVector = function () {
216
217			this.rotationVector.x = ( - this.moveState.pitchDown + this.moveState.pitchUp );
218			this.rotationVector.y = ( - this.moveState.yawRight + this.moveState.yawLeft );
219			this.rotationVector.z = ( - this.moveState.rollRight + this.moveState.rollLeft );
220
221			//console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] );
222
223		};
224
225		this.getContainerDimensions = function () {
226
227			if ( this.domElement != document ) {
228
229				return {
230					size: [ this.domElement.offsetWidth, this.domElement.offsetHeight ],
231					offset: [ this.domElement.offsetLeft, this.domElement.offsetTop ]
232				};
233
234			} else {
235
236				return {
237					size: [ window.innerWidth, window.innerHeight ],
238					offset: [ 0, 0 ]
239				};
240
241			}
242
243		};
244
245		this.dispose = function () {
246
247			this.domElement.removeEventListener( 'contextmenu', contextmenu );
248			this.domElement.removeEventListener( 'pointerdown', _pointerdown );
249			this.domElement.removeEventListener( 'pointermove', _pointermove );
250			this.domElement.removeEventListener( 'pointerup', _pointerup );
251
252			window.removeEventListener( 'keydown', _keydown );
253			window.removeEventListener( 'keyup', _keyup );
254
255		};
256
257		const _pointermove = this.pointermove.bind( this );
258		const _pointerdown = this.pointerdown.bind( this );
259		const _pointerup = this.pointerup.bind( this );
260		const _keydown = this.keydown.bind( this );
261		const _keyup = this.keyup.bind( this );
262
263		this.domElement.addEventListener( 'contextmenu', contextmenu );
264		this.domElement.addEventListener( 'pointerdown', _pointerdown );
265		this.domElement.addEventListener( 'pointermove', _pointermove );
266		this.domElement.addEventListener( 'pointerup', _pointerup );
267
268		window.addEventListener( 'keydown', _keydown );
269		window.addEventListener( 'keyup', _keyup );
270
271		this.updateMovementVector();
272		this.updateRotationVector();
273
274	}
275
276}
277
278function contextmenu( event ) {
279
280	event.preventDefault();
281
282}
283
284export { FlyControls };

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.