如何在main.js中监测Head实例的CSS位置变化并执行对应操作?
你现在的问题核心不是没法获取Head的位置,而是碰撞检测代码只执行了一次——在页面加载完成的瞬间,之后Head每500ms移动一次,但没有再触发位置判断。而且完全不需要用jQuery监听CSS变化,因为位置变化是我们自己的代码主动触发的,直接在移动逻辑里加入检测更高效可靠。
问题分析
看你的main.js,这段碰撞检测代码只在$(document).ready()里运行了一次:
let position = head.node.position(); if (position.top === apple.randomTop && position.left === apple.randomLeft) { this.endGame(); }
而Head的位置是每隔500ms通过move()方法更新的,所以页面加载后的位置变化根本不会触发这段判断。
解决方案
有两种简单可靠的方式实现实时碰撞检测:
方案1:在Head的move()方法中加入碰撞检测
每次Head移动完成后,立刻检查是否和Apple位置重合。这种方式最直接,因为移动逻辑本身就控制着位置变化:
class Head { constructor($el, apple) { this.node = $('<div id="head"></div>'); this.currentDirection = 'right'; this.SPEED = 500; this.apple = apple; // 接收传入的apple实例 $el.append(this.node); this.node.css({ top: 0, left: 0 }); this.toBeCleared = setTimeout(this.move.bind(this), this.SPEED); } move() { let direction = this.currentDirection; let position = this.node.position(); // 原边界检测逻辑不变 if (position.left === 700 || position.top === 700 || position.left === -50 || position.top === -50) { console.log('Game Over'); this.endGame(); } // 更新位置逻辑不变 if (direction === 'right') { position.left += 50; } else if (direction === 'left') { position.left -= 50; } else if (direction === 'up') { position.top -= 50; } else if (direction === 'down') { position.top += 50; } this.node.css(position); // 新增:碰撞检测 if (position.top === this.apple.randomTop && position.left === this.apple.randomLeft) { this.endGame(); } this.toBeCleared = setTimeout(this.move.bind(this), this.SPEED); } endGame() { console.log('game has ended') clearTimeout(this.toBeCleared); // 修复:加上this访问实例属性 } }
然后在main.js初始化时把apple传给Head:
$(document).ready(function() { const apple = new Apple($('#board')); const head = new Head($('#board'), apple); // 传入apple实例 // 原键盘事件逻辑不变 $('body').on('keydown', function(e) { if (e.keyCode === 37) { console.log('pressed left'); head.currentDirection = 'left'; } else if (e.keyCode === 39) { console.log('pressed right'); head.currentDirection = 'right'; } else if (e.keyCode === 38) { console.log('pressed up'); head.currentDirection = 'up'; } else if (e.keyCode === 40) { console.log('pressed down'); head.currentDirection = 'down'; } }); });
方案2:在main.js中设置定时检测
如果不想修改Head类的结构,可以在main.js里设置一个和Head移动速度一致的定时器,每隔500ms检查一次位置:
$(document).ready(function() { const head = new Head($('#board')); const apple = new Apple($('#board')); // 原键盘事件逻辑不变 $('body').on('keydown', function(e) { // ... 原代码不变 }); // 新增定时检测 const checkCollision = setInterval(() => { let position = head.node.position(); if (position.top === apple.randomTop && position.left === apple.randomLeft) { head.endGame(); clearInterval(checkCollision); // 碰撞后清除定时器 } }, 500); // 和Head的移动速度保持一致 });
这种方式不需要修改Head类,但要记得在游戏结束时清除定时器,避免不必要的后台执行。
额外修复:endGame方法的bug
你的endGame()方法里clearTimeout(toBeCleared)会报错,因为toBeCleared是Head实例的属性,必须用this.toBeCleared来访问,已经在方案1里修复了这个问题。
为什么不推荐监听CSS变化?
监听DOM的CSS变化(比如用MutationObserver或者jQuery的老方法)属于被动监听,不仅效率低,而且容易出现延迟或者不必要的触发。既然我们的代码主动控制着Head的移动,直接在移动逻辑中加入检测是最简洁、高效的方案。
内容的提问来源于stack exchange,提问作者tryingCode

