我用javascript创建了一个游戏。代码放在页面的底部。把它形象化,就好像游戏在一个页脚的下面。当用户选择开始游戏按钮时,游戏就开始了。游戏的机制是由箭头键控制的。箭头键用来上下左右移动。问题是当我按向上或向下键时,页面也滚动了。如何停止这个?我希望游戏运行时页面不滚动。我附上了我创建的游戏代码。这是一个蛇游戏。也有CSS,但我没有附上它,因为我不认为它相关
<div id="app" class="app">
<div class="start-screen">
<h2>🦊 </h2>
<div class="options">
<h3>Choose Difficulty</h3>
<p class="end-score"></p>
<button data-difficulty="100" class="active">Easy</button>
<button data-difficulty="75">Medium</button>
<button data-difficulty="50">Hard</button>
</div>
<button class="play-btn">Play</button>
</div>
<canvas id="board"></canvas>
<div class="score">0</div>
</div>
<script>
class SnakeGame {
constructor() {
this.$app = document.querySelector('#app');
this.$canvas = this.$app.querySelector('canvas');
this.ctx = this.$canvas.getContext('2d');
this.$startScreen = this.$app.querySelector('.start-screen');
this.$score = this.$app.querySelector('.score');
this.settings = {
canvas: {
width: 500,
height: 500,
background: 'white',
border: 'black'
},
snake: {
size: 20,
background: '#73854A',
border: '#000'
}
};
this.game = {
// "direction" (set in setUpGame())
// "nextDirection" (set in setUpGame())
// "score" (set in setUpGame())
speed: 100,
keyCodes: {
38: 'up',
40: 'down',
39: 'right',
37: 'left'
}
}
this.soundEffects = {
score: new Audio('./sounds/score.mp3'),
gameOver: new Audio('./sounds/game-over.mp3')
};
this.setUpGame();
this.init();
}
init() {
// Choose difficulty
// Rather than using "this.$startScreen.querySelectorAll('button')" and looping over the node list
// and attaching seperate event listeners on each item, it's more efficient to just listen in on the container and run a check at runtime
this.$startScreen.querySelector('.options').addEventListener('click', event => {
this.chooseDifficulty(event.target.dataset.difficulty);
});
// Play
this.$startScreen.querySelector('.play-btn').addEventListener('click', () => {
this.startGame();
});
}
chooseDifficulty(difficulty) {
if(difficulty) {
this.game.speed = difficulty;
this.$startScreen.querySelectorAll('.options button').forEach(btn => btn.classList.remove('active'));
event.target.classList.add('active');
}
}
setUpGame() {
// The snake starts off with 5 pieces
// Each piece is 30x30 pixels
// Each following piece must be n times as far from the first piece
const x = 300;
const y = 300;
this.snake = [
{ x: x, y: y },
{ x: x - this.settings.snake.size, y: y },
{ x: x - (this.settings.snake.size * 2), y: y },
{ x: x - (this.settings.snake.size * 3), y: y },
{ x: x - (this.settings.snake.size * 4), y: y }
];
this.food = {
active: false,
background: '#EC5E0B',
border: '#73AA24',
coordinates: {
x: 0,
y: 0
}
};
this.game.score = 0;
this.game.direction = 'right';
this.game.nextDirection = 'right';
}
startGame() {
// Stop the game over sound effect if a new game was restarted quickly before it could end
this.soundEffects.gameOver.pause();
this.soundEffects.gameOver.currentTime = 0;
// Reset a few things from the prior game
this.$app.classList.add('game-in-progress');
this.$app.classList.remove('game-over');
this.$score.innerText = 0;
this.generateSnake();
this.startGameInterval = setInterval(() => {
if(!this.detectCollision()) {
this.generateSnake();
} else {
this.endGame();
}
}, this.game.speed);
// Change direction
document.addEventListener('keydown', event => {
this.changeDirection(event.keyCode);
});
}
changeDirection(keyCode) {
const validKeyPress = Object.keys(this.game.keyCodes).includes(keyCode.toString()); // Only allow (up|down|left|right)
if(validKeyPress && this.validateDirectionChange(this.game.keyCodes[keyCode], this.game.direction)) {
this.game.nextDirection = this.game.keyCodes[keyCode];
}
}
// When already moving in one direction snake shouldn't be allowed to move in the opposite direction
validateDirectionChange(keyPress, currentDirection) {
return (keyPress === 'left' && currentDirection !== 'right') ||
(keyPress === 'right' && currentDirection !== 'left') ||
(keyPress === 'up' && currentDirection !== 'down') ||
(keyPress === 'down' && currentDirection !== 'up');
}
resetCanvas() {
// Full screen size
this.$canvas.width = this.settings.canvas.width;
this.$canvas.height = this.settings.canvas.height;
this.$canvas.style.border = `3px solid ${this.settings.canvas.border}`;
// Background
this.ctx.fillStyle = this.settings.canvas.background;
this.ctx.fillRect(0, 0, this.$canvas.width, this.$canvas.height);
}
generateSnake() {
let coordinate;
switch(this.game.direction) {
case 'right':
coordinate = {
x: this.snake[0].x + this.settings.snake.size,
y: this.snake[0].y
};
break;
case 'up':
coordinate = {
x: this.snake[0].x,
y: this.snake[0].y - this.settings.snake.size
};
break;
case 'left':
coordinate = {
x: this.snake[0].x - this.settings.snake.size,
y: this.snake[0].y
};
break;
case 'down':
coordinate = {
x: this.snake[0].x,
y: this.snake[0].y + this.settings.snake.size
};
}
// The snake moves by adding a piece to the beginning "this.snake.unshift(coordinate)" and removing the last piece "this.snake.pop()"
// Except when it eats the food in which case there is no need to remove a piece and the added piece will make it grow
this.snake.unshift(coordinate);
this.resetCanvas();
const ateFood = this.snake[0].x === this.food.coordinates.x && this.snake[0].y === this.food.coordinates.y;
if(ateFood) {
this.food.active = false;
this.game.score += 10;
this.$score.innerText = this.game.score;
this.soundEffects.score.play();
} else {
this.snake.pop();
}
this.generateFood();
this.drawSnake();
}
drawSnake() {
const size = this.settings.snake.size;
this.ctx.fillStyle = this.settings.snake.background;
this.ctx.strokestyle = this.settings.snake.border;
// Draw each piece
this.snake.forEach(coordinate => {
this.ctx.fillRect(coordinate.x, coordinate.y, size, size);
this.ctx.strokeRect(coordinate.x, coordinate.y, size, size);
});
this.game.direction = this.game.nextDirection;
}
generateFood() {
// If there is uneaten food on the canvas there's no need to regenerate it
if(this.food.active) {
this.drawFood(this.food.coordinates.x, this.food.coordinates.y);
return;
}
const gridSize = this.settings.snake.size;
const xMax = this.settings.canvas.width - gridSize;
const yMax = this.settings.canvas.height - gridSize;
const x = Math.round((Math.random() * xMax) / gridSize) * gridSize;
const y = Math.round((Math.random() * yMax) / gridSize) * gridSize;
// Make sure the generated coordinates do not conflict with the snake's present location
// If so recall this method recursively to try again
this.snake.forEach(coordinate => {
const foodSnakeConflict = coordinate.x == x && coordinate.y == y;
if(foodSnakeConflict) {
this.generateFood();
} else {
this.drawFood(x, y);
}
});
}
drawFood(x, y) {
const size = this.settings.snake.size;
this.ctx.fillStyle = this.food.background;
this.ctx.strokestyle = this.food.border;
this.ctx.fillRect(x, y, size, size);
this.ctx.strokeRect(x, y, size, size);
this.food.active = true;
this.food.coordinates.x = x;
this.food.coordinates.y = y;
}
detectCollision() {
// Self collison
// It's impossible for the first 3 pieces of the snake to self collide so the loop starts at 4
for(let i = 4; i < this.snake.length; i++) {
const selfCollison = this.snake[i].x === this.snake[0].x && this.snake[i].y === this.snake[0].y;
if(selfCollison) {
return true;
}
}
// Wall collison
const leftCollison = this.snake[0].x < 0;
const topCollison = this.snake[0].y < 0;
const rightCollison = this.snake[0].x > this.$canvas.width - this.settings.snake.size;
const bottomCollison = this.snake[0].y > this.$canvas.height - this.settings.snake.size;
return leftCollison || topCollison || rightCollison || bottomCollison;
}
endGame() {
this.soundEffects.gameOver.play();
clearInterval(this.startGameInterval);
this.$app.classList.remove('game-in-progress');
this.$app.classList.add('game-over');
this.$startScreen.querySelector('.options h3').innerText = 'Game Over';
this.$startScreen.querySelector('.options .end-score').innerText = `Score: ${this.game.score}`;
this.setUpGame();
}
}
const snakeGame = new SnakeGame();
</script>
当游戏处于活动状态时,停止页面上下移动
1条答案
按热度按时间bq8i3lrv1#
您只需要防止这些键的默认行为:
请注意,这样做并不是一个好主意,因为它会阻止页面上 * 所有 * 按键 * 任何地方 * 的默认行为。最值得注意的是,F5永远不会刷新浏览器,如果您有其他元素,如按钮,链接等,用于激活那些的标准按键将停止工作(如果你想让你的网站对那些有残疾的人无障碍,关闭这些是一个特别的问题)。
您最好先检查keyCode,并且只在它对应于其中一个箭头键时才防止默认值--这对于您的代码来说并不容易,但我鼓励您以这种方式重写它。
即使这样做也会给那些不会使用鼠标的人带来问题,因为(取决于你的游戏界面在屏幕上的位置和大小)他们可能仍然需要向下滚动,而
preventDefault
行为会阻止他们这样做。但这正是你所要求的,我已经给了你解决方案-但有一个重要的警告。