CSS/Javascript 中的移动元素与静态元素相交

Moving element intersecting static element in CSS/Javascript

Video demo

我正在尝试使用 HTML、CSS 和 Javascript 为一个项目制作盗版 pacman(ish) 游戏。查看随附的视频以供参考。这个想法是:每当食物元素之一与 pacman 元素相交(即被吃掉)时,分数就会增加 1。每当食物元素离开视口而没有通过 pacman 元素时,玩家将失去 1 条生命。我尝试使用 Intersection Observer API 来实现这一点,方法是观察食物元素作为目标并将 pacman 元素作为根元素。但这没有用。

const obsCB1 = function (entries, observer) {
  point++;
  console.log(point);
};
const obsOptions1 = {
  root: pacman,
  threshold: 0.01,
};
const obs1 = new IntersectionObserver(obsCB1, obsOptions1);
obs1.observe(f1);

(食物元素最初从视口外向右开始,并在无限循环中使用 translateX 向左移动) 即使 f1 没有与 pacman 相交,回调也会在开始时被调用一次。而且这一点后来也没有改变。我怎样才能实现这个功能?

假设吃豆子和圆点都是圆,我想最好的办法是计算中心之间的距离并检查它是否小于半径之和

const board = document.querySelector('#board');
const score = board.querySelector('#score-val');
let dots = [];
function updateScore(x){score.innerText = +score.innerText+x}
function Dot(){
  this.SPEED = (.008 + Math.random()/1000) * window.innerHeight;
  this.RADIUS = .02 * window.innerHeight;
  this.el = document.createElement('span');
  this.el.style = `display: inline-block;width:${2*this.RADIUS}px;height:${2*this.RADIUS}px;position:absolute;left:${window.innerWidth}px;top:${Math.random()*(window.innerHeight-2*this.RADIUS)}px;border-radius:50%;background-color: red;`;
  board.appendChild(this.el);
  dots.push(this);
  this.remove = () => {
    dots = dots.filter(d => d!== this);
    this.el.remove();
  }
  this.move = () => {
    this.el.style.left = parseFloat(this.el.style.left)-this.SPEED+'px';
    if(pacmen.isCollapse(this)){
      this.remove();
      updateScore(1);
    } else 
    if(parseFloat(this.el.style.left)<0) { this.remove();
    updateScore(-1);
    }
  }
}
function Pacmen(){
  this.SPEED = .05 * window.innerHeight;
  this.RADIUS = .07 * window.innerHeight;
  this.el = board.querySelector('#pacmen');
  this.el.style.left=0;
  this.el.style.top=0;
  this.el.width=this.RADIUS*2;
  this.el.height=this.RADIUS*2;
  this.move = x => {
  this.el.style.top = Math.min(window.innerHeight-this.RADIUS*2, Math.max(0, parseFloat(this.el.style.top)+x*this.SPEED))+'px';
  }
  window.addEventListener('keydown', (e)=> {
  switch(e.key){
    case 'ArrowDown':
      e.preventDefault();
      this.move(1);
      break;
    case 'ArrowUp':
      e.preventDefault();
      this.move(-1);
      break;
  }
  });
  this.isCollapse = (dot) => {
    return Math.sqrt(Math.pow(parseFloat(this.el.style.left + this.RADIUS)-parseFloat(dot.el.style.left + dot.RADIUS),2)+Math.pow(parseFloat(this.el.style.top + this.RADIUS)-parseFloat(dot.el.style.top + dot.RADIUS),2)) <= this.RADIUS + dot.RADIUS
  }
}
const pacmen = new Pacmen();
function frame(){
  dots.forEach(dot =>dot.move());
  requestAnimationFrame(frame);
}
setInterval(()=>new Dot(), 1000);
frame();
html,body{padding:0;margin:0}
#board{
display:block;
width:100%;
height:100vh;
background-color:black;
position: relative;
overflow:hidden;
}
#pacmen {
  position: absolute;
}
#score{
  position:absolute;
  bottom: 0px;
  right: 20px;
  color: white;
  font-family: sans-serif;
}
<div id="board">
<img id="pacmen" src="data:image/gif;base64,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"/>
<h3 id="score">Score: <span id="score-val">0</span><h3>
</div>