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); }