You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让我石头剪刀布小游戏中Javascript的addEventListener正常生效?

问题原因分析
    1. 代码执行顺序错误:你在构造函数Scissors/Rock/Paper定义完成之前就执行了实例化操作,JS解释执行时会直接抛出引用错误,后续的事件绑定逻辑全部没有生效,这是wasd按键无响应的核心原因。
    1. HTML结构存在语法错误:
    • 内容标签<section>错误放在了<head>标签内,<head>仅允许存放元数据、样式、标题等非内容元素
    • 没有声明class为myCanvas的canvas元素,导致JS中获取canvas节点失败,所有绘图逻辑直接报错
    • <style>标签内混入了</section>的HTML标签,导致样式解析错误
    1. 事件监听逻辑冗余:你给三个对象都单独绑定了keydown事件,每次按键三个对象的坐标都会同时修改,虽然当前仅显示激活对象,但逻辑存在不必要的开销,且容易出现预期外的问题
    1. 动画循环存在无效代码:loop函数中使用this.x/this.y的两行代码无实际作用,loop全局调用时this指向window,对应的取值都是undefined。
修复步骤

第一步:修复HTML结构

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Canvas石头剪刀布小游戏</title>
  <style>
    * {
      box-sizing: border-box;
    }
    section {
      width: 100%;
      height: inherit;
      padding: 30px;
    }
    .topbar {
      height: 50%;
      display: flex;
      justify-content: space-between;
      color: #fff;
    }
    .topbar p,
    body {
      margin: 0;
      overflow: hidden;
      background: #000;
    }
    .myCanvas {
      display: block;
    }
  </style>
</head>
<body>
  <section>
    <div class="topbar">
      <p class="key">1: 石头</p>
      <p class="key">2: 布</p>
      <p class="key">3: 剪刀</p>
    </div>
  </section>
  <canvas class="myCanvas" width="800" height="600"></canvas>
  <script>
  // 这里放修复后的JS代码
  </script>
</body>
</html>

第二步:调整JS执行顺序并优化逻辑

const canvas = document.querySelector('.myCanvas');
const width = canvas.width;
const height = canvas.height;
const ctx = canvas.getContext('2d');
canvas.setAttribute("tabindex", 0);

let objects = [];
// 先定义构造函数
function userShape(x, y, velX, velY, exists) {
  this.x = x;
  this.y = y;
  this.velX = velX;
  this.velY = velY;
  this.exists = exists;
}

function Scissors(x, y, velX, velY, exists) {
  userShape.call(this, x, y, velX, velY, exists);
}

function Rock(x, y, velX, velY, exists) {
  userShape.call(this, x, y, velX, velY, exists);
}

function Paper(x, y, velX, velY, exists) {
  userShape.call(this, x, y, velX, velY, exists);
}
// 定义原型方法
Scissors.prototype.draw = function() {
  let scissorLength = 130
  ctx.fillStyle = 'rgba(0, 0, 255, 0.9)';
  ctx.fillRect(this.x, this.y, scissorLength, 15);
  ctx.fillRect(this.x, this.y - 30, scissorLength, 15);
  ctx.strokeStyle = 'rgba(0, 0, 255, 0.9)';
  ctx.beginPath();
  ctx.arc(this.x, this.y - 20, 20, degToRad(0), degToRad(360), false);
  ctx.lineWidth = 5;
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(this.x, this.y, 20, degToRad(0), degToRad(360), false);
  ctx.lineWidth = 5;
  ctx.stroke();
}
Rock.prototype.draw = function() {
  let radius = 53;
  ctx.fillStyle = 'rgba(127, 127, 127, 0.9)';
  ctx.beginPath();
  ctx.arc(this.x, this.y, radius, degToRad(0), degToRad(360), false);
  ctx.fill();
}
Paper.prototype.draw = function() {
  ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
  ctx.fillRect(this.x, this.y, 150, 150);
}
// 实例化对象
let scissors = new Scissors(width / 2, height / 2, 10, 10, false);
let rock = new Rock(width / 2, height / 2, 10, 10, false);
let paper = new Paper(width / 2, height / 2, 10, 10, false);
objects.push(scissors, rock, paper);

// 统一绑定按键事件
document.addEventListener('keydown', function(e) {
  // 处理切换逻辑
  if (e.key === '1') {
    rock.exists = true;
    paper.exists = false;
    scissors.exists = false;
    return;
  } else if (e.key === '2') {
    rock.exists = false;
    paper.exists = true;
    scissors.exists = false;
    return;
  } else if (e.key === '3') {
    rock.exists = false;
    paper.exists = false;
    scissors.exists = true;
    return;
  }
  // 处理移动逻辑
  let activeObj = objects.find(item => item.exists);
  if (!activeObj) return;
  if (e.key === 'a') {
    activeObj.x -= activeObj.velX;
  } else if (e.key === 'd') {
    activeObj.x += activeObj.velX;
  } else if (e.key === 'w') {
    activeObj.y -= activeObj.velY;
  } else if (e.key === 's') {
    activeObj.y += activeObj.velY;
  }
});

// 动画循环
function loop() {
  ctx.clearRect(0, 0, width, height);
  ctx.fillStyle = 'rgb(0,0,0)';
  ctx.fillRect(0, 0, width, height);
  for (let i = 0; i < objects.length; i++){
    if(objects[i].exists){
      objects[i].draw();
    }
  }
  requestAnimationFrame(loop);
};
loop();

function degToRad(degrees) {
  return degrees * Math.PI / 180;
};

你可以根据需要调整对象的移动速度(实例化时的velX、velY参数)、尺寸等参数。

内容的提问来源于stack exchange,提问作者jakepow07

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 18:15:03