icy3dw/src/client/object/joystick.ts

102 lines
2.9 KiB
TypeScript

import { Vector2 } from 'three';
import { Player } from './player';
export class Joystick {
public element = document.createElement('div');
private knob = document.createElement('div');
private mousePos = new Vector2();
private prevMousePos = new Vector2();
private center = new Vector2();
private knobCenter = new Vector2();
private mouseCenterOffset = new Vector2();
private radiusSquare = new Vector2();
private negRadiusSquare = new Vector2();
private appliedForce = new Vector2();
private dragging = false;
constructor(private player: Player, public radius = 60) {}
initialize() {
this.element.classList.add('joystick', 'joystick__wrapper');
this.knob.classList.add('joystick__knob');
this.element.append(this.knob);
document.body.append(this.element);
this.element.addEventListener('touchstart', (e) => {
e.preventDefault();
const touch = e.touches[0] || e.changedTouches[0];
this.mousePos.fromArray([touch.pageX, touch.pageY]);
this.dragging = true;
});
this.element.addEventListener('touchmove', (e) => {
e.preventDefault();
if (this.dragging) {
this.prevMousePos.copy(this.mousePos);
this.mousePos.fromArray([e.touches[0].clientX, e.touches[0].clientY]);
this.mouseCenterOffset.copy(this.center).sub(this.mousePos);
this.mouseCenterOffset.clamp(this.negRadiusSquare, this.radiusSquare);
const knobPosition = new Vector2();
knobPosition.copy(this.knobCenter).sub(this.mouseCenterOffset);
this.knob.style.transform = `translate(${knobPosition.x}px, ${knobPosition.y}px)`;
this.appliedForce
.copy(this.mouseCenterOffset)
.divideScalar(this.radius);
}
});
this.element.addEventListener('touchend', (e) => {
this.dragging = false;
this.centerKnob();
});
window.addEventListener('resize', this._windowEventBound);
this.getCenter();
this.centerKnob();
}
getCenter() {
const rect = this.element.getBoundingClientRect();
this.center.fromArray([rect.left + this.radius, rect.top + this.radius]);
this.knobCenter.fromArray([this.radius / 2 - 2, this.radius / 2 - 2]);
}
centerKnob() {
this.appliedForce.set(0, 0);
this.knob.style.transform = `translate(${this.knobCenter.x}px, ${this.knobCenter.y}px)`;
}
reset() {
this.radiusSquare.set(this.radius, this.radius);
this.negRadiusSquare.set(-this.radius, -this.radius);
this.element.style.setProperty('--size', `${this.radius * 2}px`);
this.getCenter();
}
update(dt: number) {
if (this.appliedForce.length() !== 0) {
this.player.applyForce(this.appliedForce);
}
}
dispose() {
this.element.parentElement.removeChild(this.element);
}
show() {
this.element.style.display = 'block';
this.reset();
}
private _windowEvent() {
this.reset();
}
private _windowEventBound = this._windowEvent.bind(this);
}