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

Canvas随机生成圆形对象的鼠标触碰消失功能实现问题

Canvas游戏:鼠标触碰圆形消失的实现方案

核心实现逻辑

  • 用数组存储所有圆形对象,每个对象包含圆心坐标x、y和半径radius
  • 监听鼠标事件,把页面鼠标坐标转换成Canvas内部的坐标(解决Canvas偏移导致的坐标不准问题)
  • 遍历圆形数组,计算鼠标与每个圆心的距离平方(比开平方更高效),判断是否触碰
  • 移除触碰的圆形,立即重绘画布

你的两种尝试问题分析

Attempt1 偶尔生效的原因

大概率是鼠标坐标没转换,直接用了event.clientX/event.clientY,但如果Canvas有margin、padding或者定位偏移,这个坐标和Canvas内部的坐标系不匹配,导致碰撞检测偶尔才对。另外也可能是遍历数组时用了正向遍历,删除元素后跳过了下一个圆形。

Attempt2 箭头函数错误+无法生成圆形的原因

箭头函数的this指向是定义时的上下文,如果你在生成圆形的函数里用了箭头函数,比如const generateCircle = () => {...},如果这个函数里用到了this(比如指向Canvas),会导致this指向全局对象,圆形属性没正确赋值,数组里没添加有效元素;另外老版本浏览器可能不支持箭头函数,也会报错。

完整可运行代码

HTML部分

<canvas id="gameCanvas" width="800" height="600" style="border:1px solid #000;"></canvas>

JavaScript部分

const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

// 存所有圆形的数组
const circles = [];

// 生成随机圆形
function generateCircle() {
  const radius = Math.random() * 30 + 10; // 半径10-40px
  // 确保圆形不会超出画布边界
  const x = Math.random() * (canvas.width - radius * 2) + radius;
  const y = Math.random() * (canvas.height - radius * 2) + radius;
  circles.push({ x, y, radius });
}

// 渲染所有圆形
function renderCircles() {
  // 先清空画布
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  circles.forEach(circle => {
    ctx.beginPath();
    ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
    ctx.fillStyle = '#4285F4';
    ctx.fill();
    ctx.closePath();
  });
}

// 把页面鼠标坐标转成Canvas内部坐标
function getCanvasMousePos(event) {
  const rect = canvas.getBoundingClientRect();
  return {
    x: event.clientX - rect.left,
    y: event.clientY - rect.top
  };
}

// 判断鼠标是否在圆形内
function isTouchingCircle(mousePos, circle) {
  const dx = mousePos.x - circle.x;
  const dy = mousePos.y - circle.y;
  // 用平方比较,避免开平方,性能更好
  return dx * dx + dy * dy <= circle.radius * circle.radius;
}

// 监听鼠标移动事件(换成click就是点击消失)
canvas.addEventListener('mousemove', (event) => {
  const mousePos = getCanvasMousePos(event);
  // 从后往前遍历数组,避免删除元素后跳过下一个
  for (let i = circles.length - 1; i >= 0; i--) {
    if (isTouchingCircle(mousePos, circles[i])) {
      circles.splice(i, 1);
      renderCircles(); // 移除后立即重绘
    }
  }
});

// 初始化生成10个圆形
for (let i = 0; i < 10; i++) {
  generateCircle();
}
renderCircles();

// 可选:每2秒自动生成新圆形
setInterval(() => {
  generateCircle();
  renderCircles();
}, 2000);

必踩坑提醒

  1. 坐标转换一定要做:不管Canvas有没有样式偏移,都要用getBoundingClientRect()转坐标,否则碰撞检测肯定不准。
  2. 遍历数组要从后往前:如果正向遍历,删除一个元素后,数组长度变短,下一个元素的索引会被跳过,导致有些圆形没检测到。
  3. 箭头函数别乱用:在需要特定this指向的场景(比如构造函数、对象方法)别用箭头函数,事件监听和定时器里用没问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 13:38:29