Skip to content

Instantly share code, notes, and snippets.

@jcampuza
Created April 10, 2021 18:03
Show Gist options
  • Save jcampuza/45b0de8f91608310a877a2b45e9d9099 to your computer and use it in GitHub Desktop.
Save jcampuza/45b0de8f91608310a877a2b45e9d9099 to your computer and use it in GitHub Desktop.
FirstPersonControls for three.js
import { Camera, Euler } from 'three';
export class FirstPersonControls {
constructor(private camera: Camera, private domElement: HTMLElement) {
this.connect();
}
state = {
mousedown: false,
enabled: false,
previousPointerX: 0,
previousPointerY: 0,
};
set enabled(value: boolean) {
this.state.enabled = value;
}
connect() {
this.domElement.addEventListener('mousedown', this.onPointerdown);
this.domElement.addEventListener('mouseup', this.onPointerup);
this.domElement.addEventListener('mouseout', this.onPointerup);
this.domElement.addEventListener('mousemove', this.onPointerMove);
this.domElement.addEventListener('touchstart', this.onPointerdown);
this.domElement.addEventListener('touchend', this.onPointerup);
this.domElement.addEventListener('touchcancel', this.onPointerup);
this.domElement.addEventListener('touchmove', this.onPointerMove);
}
disconnect() {
this.domElement.removeEventListener('mousedown', this.onPointerdown);
this.domElement.removeEventListener('mouseout', this.onPointerup);
this.domElement.removeEventListener('mouseup', this.onPointerup);
this.domElement.removeEventListener('mousemove', this.onPointerMove);
this.domElement.removeEventListener('touchstart', this.onPointerdown);
this.domElement.removeEventListener('touchend', this.onPointerup);
this.domElement.removeEventListener('touchcancel', this.onPointerup);
this.domElement.removeEventListener('touchmove', this.onPointerMove);
}
getPointerClientPosition(e: MouseEvent | TouchEvent) {
return {
clientX: e instanceof MouseEvent ? e.clientX : e.touches[0].clientX,
clientY: e instanceof MouseEvent ? e.clientY : e.touches[0].clientY,
};
}
onPointerdown = (e: MouseEvent | TouchEvent) => {
if (!this.state.enabled) {
return;
}
e.preventDefault();
e.stopPropagation();
this.state.mousedown = true;
const { clientX, clientY } = this.getPointerClientPosition(e);
this.state.previousPointerX = clientX;
this.state.previousPointerY = clientY;
};
onPointerup = () => {
if (!this.state.enabled) {
return;
}
this.state.mousedown = false;
this.state.previousPointerX = 0;
this.state.previousPointerY = 0;
};
onPointerMove = (e: TouchEvent | MouseEvent) => {
if (!this.state.enabled) {
return;
}
if (!this.state.mousedown) {
return;
}
const { clientX, clientY } = this.getPointerClientPosition(e);
const delta = {
x: clientX - this.state.previousPointerX,
y: clientY - this.state.previousPointerY,
};
this.state.previousPointerX = clientX;
this.state.previousPointerY = clientY;
const euler = new Euler(0, 0, 0, 'YXZ');
euler.setFromQuaternion(this.camera.quaternion);
euler.y += delta.x * 0.003;
euler.x += delta.y * 0.003;
const HALF_PI = Math.PI / 2;
euler.x = Math.max(-HALF_PI, Math.min(HALF_PI, euler.x));
this.camera.quaternion.setFromEuler(euler);
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment