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);
必踩坑提醒
- 坐标转换一定要做:不管Canvas有没有样式偏移,都要用
getBoundingClientRect()转坐标,否则碰撞检测肯定不准。 - 遍历数组要从后往前:如果正向遍历,删除一个元素后,数组长度变短,下一个元素的索引会被跳过,导致有些圆形没检测到。
- 箭头函数别乱用:在需要特定
this指向的场景(比如构造函数、对象方法)别用箭头函数,事件监听和定时器里用没问题。
内容的提问来源于stack exchange,提问作者BlueClub
相关产品推荐
相关产品推荐

