如何让我石头剪刀布小游戏中Javascript的addEventListener正常生效?
问题原因分析
- 代码执行顺序错误:你在构造函数
Scissors/Rock/Paper定义完成之前就执行了实例化操作,JS解释执行时会直接抛出引用错误,后续的事件绑定逻辑全部没有生效,这是wasd按键无响应的核心原因。
- 代码执行顺序错误:你在构造函数
- HTML结构存在语法错误:
- 内容标签
<section>错误放在了<head>标签内,<head>仅允许存放元数据、样式、标题等非内容元素 - 没有声明class为
myCanvas的canvas元素,导致JS中获取canvas节点失败,所有绘图逻辑直接报错 <style>标签内混入了</section>的HTML标签,导致样式解析错误
- 事件监听逻辑冗余:你给三个对象都单独绑定了keydown事件,每次按键三个对象的坐标都会同时修改,虽然当前仅显示激活对象,但逻辑存在不必要的开销,且容易出现预期外的问题
- 动画循环存在无效代码:
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
相关产品推荐
相关产品推荐

