icy3dw/src/client/object/joystick.ts

131 lines
3.7 KiB
TypeScript

import { Vector2 } from 'three';
import { Player } from './player';
export class Joystick {
public element = document.createElement('div');
private _inner = 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 knobPosition = 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__wrapper');
this._inner.classList.add('joystick');
this.knob.classList.add('joystick__knob');
this._inner.append(this.knob);
this.element.append(this._inner);
document.body.append(this.element);
this._inner.addEventListener('touchstart', (e) => {
e.preventDefault();
const touch = e.touches[0] || e.changedTouches[0];
this.mousePos.fromArray([touch.pageX, touch.pageY]);
this.dragging = true;
});
this._inner.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.clampLength(-this.radius, this.radius);
this.knobPosition.copy(this.knobCenter).sub(this.mouseCenterOffset);
this.knob.style.transform = `translate(${this.knobPosition.x}px, ${this.knobPosition.y}px)`;
this.appliedForce
.copy(this.mouseCenterOffset)
.divideScalar(this.radius);
}
});
this._inner.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();
}
public addButton(
x: number,
y: number,
text: string,
toggle: () => void | boolean,
) {
const btn = document.createElement('button');
btn.innerText = text;
btn.style.setProperty('--button-x', `${x}px`);
btn.style.setProperty('--button-y', `${y}px`);
btn.classList.add('joystick__button');
btn.addEventListener('click', (ev) => {
ev.preventDefault();
ev.stopPropagation();
const value = toggle();
if (typeof value === 'boolean') {
if (value) {
btn.classList.add('joystick__button--active');
} else {
btn.classList.remove('joystick__button--active');
}
}
});
this.element.append(btn);
}
private _windowEvent() {
this.reset();
}
private _windowEventBound = this._windowEvent.bind(this);
}