242 lines
10 KiB
JavaScript

// src/core/GameBox.ts
export class GameBox {
destroy() {
// 移除事件监听器
window.removeEventListener('resize', () => this.updateSize());
window.removeEventListener('orientationchange', () => {
setTimeout(() => this.updateSize(), 100);
});
// 移除 DOM 元素
if (this.element && this.element.parentNode) {
this.element.parentNode.removeChild(this.element);
}
}
//
constructor(config) {
this.lastBackgroundPosition = { x: 50, y: 50 }; // Track background position
this.isTouching = false;
this.currentDirection = { x: 0, y: 0 };
this.moveInterval = null;
this.aspectRatio = config.aspectRatio;
this.backgroundColor = config.backgroundColor;
this.borderColor = config.borderColor;
this.container = config.element;
this.element = this.createBoxElement();
this.ballElement = this.createBall();
this.touchRingElement = this.createTouchRing();
this.container.style.display = 'flex';
this.container.style.justifyContent = 'center';
this.container.style.alignItems = 'center';
this.container.style.width = '100vw';
this.container.style.height = '100vh';
this.container.style.overflow = 'hidden';
this.bindEvents();
this.updateSize();
}
// 删除 createContainer 方法
createBoxElement() {
const element = document.createElement('div');
element.className = 'game-box';
element.style.boxSizing = 'border-box';
element.style.backgroundSize = 'auto 100%';
element.style.backgroundRepeat = 'no-repeat';
element.style.backgroundPosition = 'center center';
element.style.display = 'flex';
element.style.justifyContent = 'center';
element.style.alignItems = 'center';
this.applyBoxStyle(element);
this.container.appendChild(element);
return element;
}
applyBoxStyle(element) {
// Create grid background with random colors
const gridSize = 200; // Size of each grid square (doubled from previous)
const gridColor = 'rgba(0, 0, 0, 0.1)';
// Create canvas for random grid colors
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 2000;
canvas.height = 2000;
// Create gradient grid pattern
const baseColor = 200; // Base gray level
const colorStep = 40; // Increased color step for larger grids
for (let x = 0; x < canvas.width; x += gridSize) {
for (let y = 0; y < canvas.height; y += gridSize) {
// Calculate gradient based on grid position
const xCycle = Math.floor((x / gridSize) % 5);
const yCycle = Math.floor((y / gridSize) % 5);
const colorValue = baseColor - (xCycle + yCycle) * colorStep;
// Ensure color stays within valid range
const finalColor = Math.max(0, Math.min(255, colorValue));
ctx.fillStyle = `rgba(${finalColor},${finalColor},${finalColor},1)`;
ctx.fillRect(x, y, gridSize, gridSize);
// Add subtle border between cells
ctx.strokeStyle = 'rgba(0,0,0,0.1)';
ctx.strokeRect(x, y, gridSize, gridSize);
}
}
// Add grid lines and coordinates
const gridImage = canvas.toDataURL();
element.style.background = `
url('${gridImage}'),
linear-gradient(to right, ${gridColor} 1px, transparent 1px),
linear-gradient(to bottom, ${gridColor} 1px, transparent 1px),
${this.backgroundColor}
`;
element.style.backgroundSize = `${gridSize}px ${gridSize}px`;
element.style.border = `1px solid ${this.borderColor}`;
// Add coordinate labels
this.addGridCoordinates(element);
}
addGridCoordinates(element) {
const gridSize = 200;
const width = 2000; // Use canvas width for consistent coordinates
const height = 2000; // Use canvas height for consistent coordinates
// X-axis labels
for (let x = 0; x < width; x += gridSize) {
const label = document.createElement('div');
label.style.position = 'absolute';
label.style.left = `${x}px`;
label.style.bottom = '-20px';
label.style.color = this.borderColor;
label.style.fontSize = '12px';
label.innerText = `${x / gridSize}`;
element.appendChild(label);
}
// Y-axis labels
for (let y = 0; y < height; y += gridSize) {
const label = document.createElement('div');
label.style.position = 'absolute';
label.style.top = `${y}px`;
label.style.left = '-20px';
label.style.color = this.borderColor;
label.style.fontSize = '12px';
label.innerText = `${y / gridSize}`;
element.appendChild(label);
}
}
createBall() {
const ball = document.createElement('div');
ball.style.position = 'absolute';
ball.style.width = '40px';
ball.style.height = '40px';
ball.style.borderRadius = '50%';
ball.style.backgroundColor = 'red';
ball.style.transform = 'translate(-50%, -50%)';
ball.style.left = '50%';
ball.style.top = '50%';
this.element.appendChild(ball);
return ball;
}
createTouchRing() {
const ring = document.createElement('div');
ring.style.position = 'absolute';
ring.style.width = '100px';
ring.style.height = '100px';
ring.style.borderRadius = '50%';
ring.style.border = '2px solid rgba(255,255,255,0.5)';
ring.style.left = '20%';
ring.style.bottom = '30%';
ring.style.background = 'rgba(245, 222, 179, 0.5)'; // wheat color with 50% opacity
ring.style.transform = 'translate(-50%, 50%)';
ring.style.pointerEvents = 'auto';
this.element.appendChild(ring);
return ring;
}
bindEvents() {
window.addEventListener('resize', () => this.updateSize());
window.addEventListener('orientationchange', () => {
setTimeout(() => this.updateSize(), 100);
});
// Add touch event listeners
this.touchRingElement.addEventListener('touchstart', this.handleTouchStart.bind(this));
this.touchRingElement.addEventListener('touchmove', this.handleTouchMove.bind(this));
this.touchRingElement.addEventListener('touchend', this.handleTouchEnd.bind(this));
}
handleTouchStart(event) {
event.preventDefault();
this.isTouching = true;
this.updateBallPosition(event.touches[0]);
// Start continuous movement
this.moveInterval = window.setInterval(() => {
if (this.isTouching) {
this.lastBackgroundPosition.x += this.currentDirection.x;
this.lastBackgroundPosition.y += this.currentDirection.y;
this.element.style.backgroundPosition = `${this.lastBackgroundPosition.x}% ${this.lastBackgroundPosition.y}%`;
}
}, 16); // ~60fps
}
handleTouchMove(event) {
event.preventDefault();
this.updateBallPosition(event.touches[0]);
}
handleTouchEnd() {
this.isTouching = false;
if (this.moveInterval) {
window.clearInterval(this.moveInterval);
this.moveInterval = null;
}
}
updateBallPosition(touch) {
const rect = this.touchRingElement.getBoundingClientRect();
const ringCenterX = rect.left + rect.width / 2;
const ringCenterY = rect.top + rect.height / 2;
const ringRadius = rect.width / 2;
// Get touch position relative to ring center
const touchX = touch.clientX - ringCenterX;
const touchY = touch.clientY - ringCenterY;
// Calculate distance from center
const distance = Math.sqrt(touchX * touchX + touchY * touchY);
const normalizedDistance = Math.min(distance / ringRadius, 1);
// Calculate speed factor
let speedFactor = 0;
if (normalizedDistance > 0.5 && normalizedDistance <= 1) {
// Map distance from 0.5-1 to 0.8-1.2 in 5% increments
const steps = (normalizedDistance - 0.5) / 0.1;
speedFactor = 0.8 + Math.floor(steps) * 0.05;
}
// Calculate direction vector
const directionX = touchX / distance;
const directionY = touchY / distance;
// Normalize direction vector to maintain equal speed in both axes
const magnitude = Math.sqrt(directionX * directionX + directionY * directionY);
const normalizedX = directionX / magnitude;
const normalizedY = directionY / magnitude;
// Update current direction for continuous movement
this.currentDirection = {
x: -normalizedX * speedFactor * 0.5, // Keep horizontal speed unchanged
y: -normalizedY * speedFactor * 1.0 // Double vertical speed
};
// Update background position based on last position
this.lastBackgroundPosition.x += this.currentDirection.x;
this.lastBackgroundPosition.y += this.currentDirection.y;
this.element.style.backgroundPosition = `${this.lastBackgroundPosition.x}% ${this.lastBackgroundPosition.y}%`;
}
updateSize() {
const visualWidth = window.innerWidth;
const visualHeight = window.innerHeight;
const windowRatio = visualWidth / visualHeight;
let boxWidth, boxHeight;
if (windowRatio > this.aspectRatio) {
// Window is wider than aspect ratio - fit to height
boxHeight = visualHeight;
boxWidth = boxHeight * this.aspectRatio;
}
else {
// Window is taller than aspect ratio - fit to width
boxWidth = visualWidth;
boxHeight = boxWidth / this.aspectRatio;
}
// Set box dimensions
this.element.style.width = `${boxWidth}px`;
this.element.style.height = `${boxHeight}px`;
this.element.style.margin = 'auto'; // Use flexbox centering
// Update touch ring position
// this.touchRingElement.style.left = '20px';
// this.touchRingElement.style.bottom = '20px';
}
setBackground(imageUrl) {
this.element.style.backgroundImage = `url('${imageUrl}')`;
}
}