95 lines
3.5 KiB
JavaScript
95 lines
3.5 KiB
JavaScript
import { Vector2 } from './Vector2';
|
|
import { Direction } from './types/TouchData';
|
|
export class TouchRing {
|
|
constructor(parent, size) {
|
|
this.parent = parent;
|
|
this.size = size;
|
|
this.isTouching = false;
|
|
this.element = document.createElement('div');
|
|
this.radius = size / 2;
|
|
this.center = new Vector2(this.radius, this.radius);
|
|
this.initElement();
|
|
this.initEvents();
|
|
}
|
|
initElement() {
|
|
this.element.style.width = `${this.size}px`;
|
|
this.element.style.height = `${this.size}px`;
|
|
this.element.style.borderRadius = '50%';
|
|
this.element.style.backgroundColor = '#5ab5da';
|
|
this.element.style.opacity = '0.7';
|
|
this.element.style.position = 'absolute';
|
|
this.element.style.left = '5%';
|
|
this.element.style.bottom = '5%';
|
|
this.element.style.touchAction = 'none';
|
|
this.parent.appendChild(this.element);
|
|
}
|
|
initEvents() {
|
|
this.element.addEventListener('touchstart', this.handleTouchStart.bind(this));
|
|
this.element.addEventListener('touchmove', this.handleTouchMove.bind(this));
|
|
this.element.addEventListener('touchend', this.handleTouchEnd.bind(this));
|
|
}
|
|
handleTouchStart(event) {
|
|
event.preventDefault();
|
|
this.isTouching = true;
|
|
}
|
|
handleTouchMove(event) {
|
|
if (!this.isTouching)
|
|
return;
|
|
event.preventDefault();
|
|
const touch = event.touches[0];
|
|
const rect = this.element.getBoundingClientRect();
|
|
const touchPos = new Vector2(touch.clientX - rect.left, touch.clientY - rect.top);
|
|
const touchData = this.calculateTouchData(touchPos);
|
|
this.dispatchTouchEvent(touchData);
|
|
}
|
|
handleTouchEnd() {
|
|
this.isTouching = false;
|
|
this.dispatchTouchEvent({
|
|
direction: Direction.None,
|
|
speedFactor: 1,
|
|
normalizedPosition: new Vector2()
|
|
});
|
|
}
|
|
calculateTouchData(touchPos) {
|
|
const offset = touchPos.subtract(this.center);
|
|
const distance = offset.length();
|
|
const clampedDistance = Math.min(distance, this.radius);
|
|
// 计算速度因子
|
|
const speedFactor = 0.8 + (clampedDistance / this.radius) * 0.4;
|
|
// 计算方向
|
|
const angle = Math.atan2(offset.y, offset.x);
|
|
const direction = this.getDirectionFromAngle(angle);
|
|
// 归一化位置
|
|
const normalizedPosition = offset.scale(1 / this.radius);
|
|
return {
|
|
direction,
|
|
speedFactor,
|
|
normalizedPosition
|
|
};
|
|
}
|
|
getDirectionFromAngle(angle) {
|
|
const pi = Math.PI;
|
|
if (angle >= -pi / 8 && angle < pi / 8)
|
|
return Direction.Right;
|
|
if (angle >= pi / 8 && angle < 3 * pi / 8)
|
|
return Direction.UpRight;
|
|
if (angle >= 3 * pi / 8 && angle < 5 * pi / 8)
|
|
return Direction.Up;
|
|
if (angle >= 5 * pi / 8 && angle < 7 * pi / 8)
|
|
return Direction.UpLeft;
|
|
if (angle >= 7 * pi / 8 || angle < -7 * pi / 8)
|
|
return Direction.Left;
|
|
if (angle >= -7 * pi / 8 && angle < -5 * pi / 8)
|
|
return Direction.DownLeft;
|
|
if (angle >= -5 * pi / 8 && angle < -3 * pi / 8)
|
|
return Direction.Down;
|
|
if (angle >= -3 * pi / 8 && angle < -pi / 8)
|
|
return Direction.DownRight;
|
|
return Direction.None;
|
|
}
|
|
dispatchTouchEvent(data) {
|
|
const event = new CustomEvent('touchChange', { detail: data });
|
|
this.element.dispatchEvent(event);
|
|
}
|
|
}
|