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

如何检测Canvas上的鼠标点击是否命中圆形?

Canvas圆形点击检测实现方案

我不确定如何实现Canvas上鼠标点击圆形的检测功能,自行查找的解决方案既不兼容现有代码,也不符合我的需求。我仅需实现点击是否命中圆形的检测逻辑,以下是我的现有代码,若需更多细节请告知。

//variable declarations
const cavna = document.getElementById("gameC");
const mainContext = cavna.getContext("2d");
var counter = 0;
var maxCanvas = {
  height: cavna.height,
  width: cavna.width,
}
//TODO:
let amountOfCirclesOnScreen = 0;
//generate a random number between the specified values
function ran(min, max) {
  return (Math.random() * (max - min) + min);
}
//generates a random color hex code
function GenerateRandomColor() {
  return Math.floor(Math.random() * 16777215).toString(16);
}
//makes a new circle
function GenerateNewCircle() {
  if (amountOfCirclesOnScreen < 1) {
    amountOfCirclesOnScreen++;
    //maybe figure out how to draw a circle (completed)
    mainContext.beginPath();
    //mainContext.moveTo(ran(0, maxCanvas.width), ran(0, maxCanvas.height));
    mainContext.fillStyle = 'hsl(' + 360 * Math.random() + ', 100%, 50%)';
    mainContext.arc(ran(0, maxCanvas.width), ran(0, maxCanvas.height), 50, 0, 2 * Math.PI);
    mainContext.stroke();
    mainContext.fill();
  }
}
//clears tjhe canvas
function clearCanvas() {
  amountOfCirclesOnScreen = 0;
  mainContext.clearRect(0, 0, maxCanvas.width, maxCanvas.height);
}

//checks where u click 
//TODO: check to see if click is on a circle, if yes, clear the canvas
function getMousePosition(event) {
  let x = maxCanvas.width - event.clientX;
  let y = maxCanvas.height - event.clientY;
  alert("Coordinate x: " + x + 
    " | Coordinate y: " + y);
}
cavna.addEventListener("mousedown", function(e) {
  getMousePosition(e);
});

GenerateNewCircle();

setInterval(() => {
  document.getElementById("counter").innerText = amountOfCirclesOnScreen;
}, 40);

解决方案

要实现点击命中圆形的检测,核心是保存圆形的坐标和半径数据,然后通过计算点击点与圆心的距离来判断是否命中。以下是对现有代码的修改:

1. 新增变量存储当前圆形信息

在全局变量区域添加:

// 存储当前显示的圆形的坐标和半径
let currentCircle = null;

2. 修改GenerateNewCircle函数,保存圆形数据

生成圆时,把圆心坐标和半径存入currentCircle:

function GenerateNewCircle() {
  if (amountOfCirclesOnScreen < 1) {
    amountOfCirclesOnScreen++;
    const circleX = ran(0, maxCanvas.width);
    const circleY = ran(0, maxCanvas.height);
    const circleRadius = 50;
    // 保存圆形数据
    currentCircle = { x: circleX, y: circleY, radius: circleRadius };
    
    mainContext.beginPath();
    mainContext.fillStyle = 'hsl(' + 360 * Math.random() + ', 100%, 50%)';
    mainContext.arc(circleX, circleY, circleRadius, 0, 2 * Math.PI);
    mainContext.stroke();
    mainContext.fill();
  }
}

3. 修正鼠标坐标计算逻辑

之前的坐标计算是错误的,需要将视口坐标转换为Canvas内部坐标:

function getMousePosition(event) {
  // 获取Canvas在页面中的位置偏移
  const rect = cavna.getBoundingClientRect();
  // 计算相对于Canvas左上角的坐标
  const x = event.clientX - rect.left;
  const y = event.clientY - rect.top;
  
  // 检测是否命中圆形
  if (currentCircle) {
    // 计算点击点到圆心的距离(勾股定理)
    const dx = x - currentCircle.x;
    const dy = y - currentCircle.y;
    const distance = Math.sqrt(dx * dx + dy * dy);
    
    // 距离小于等于半径则命中
    if (distance <= currentCircle.radius) {
      clearCanvas();
      GenerateNewCircle(); // 命中后可重新生成新圆
    }
  }
}

完整修改后的代码

//variable declarations
const cavna = document.getElementById("gameC");
const mainContext = cavna.getContext("2d");
var counter = 0;
var maxCanvas = {
  height: cavna.height,
  width: cavna.width,
}
//TODO:
let amountOfCirclesOnScreen = 0;
// 存储当前显示的圆形的坐标和半径
let currentCircle = null;

//generate a random number between the specified values
function ran(min, max) {
  return (Math.random() * (max - min) + min);
}

//generates a random color hex code
function GenerateRandomColor() {
  return Math.floor(Math.random() * 16777215).toString(16);
}

//makes a new circle
function GenerateNewCircle() {
  if (amountOfCirclesOnScreen < 1) {
    amountOfCirclesOnScreen++;
    const circleX = ran(0, maxCanvas.width);
    const circleY = ran(0, maxCanvas.height);
    const circleRadius = 50;
    // 保存圆形数据
    currentCircle = { x: circleX, y: circleY, radius: circleRadius };
    
    mainContext.beginPath();
    mainContext.fillStyle = 'hsl(' + 360 * Math.random() + ', 100%, 50%)';
    mainContext.arc(circleX, circleY, circleRadius, 0, 2 * Math.PI);
    mainContext.stroke();
    mainContext.fill();
  }
}

//clears the canvas
function clearCanvas() {
  amountOfCirclesOnScreen = 0;
  currentCircle = null; // 清空圆形数据
  mainContext.clearRect(0, 0, maxCanvas.width, maxCanvas.height);
}

//checks where u click 
function getMousePosition(event) {
  // 获取Canvas在页面中的位置偏移
  const rect = cavna.getBoundingClientRect();
  // 计算相对于Canvas左上角的坐标
  const x = event.clientX - rect.left;
  const y = event.clientY - rect.top;
  
  // 检测是否命中圆形
  if (currentCircle) {
    // 计算点击点到圆心的距离(勾股定理)
    const dx = x - currentCircle.x;
    const dy = y - currentCircle.y;
    const distance = Math.sqrt(dx * dx + dy * dy);
    
    // 距离小于等于半径则命中,清空画布并生成新圆
    if (distance <= currentCircle.radius) {
      clearCanvas();
      GenerateNewCircle();
    }
  }
}

cavna.addEventListener("mousedown", function(e) {
  getMousePosition(e);
});

GenerateNewCircle();

setInterval(() => {
  document.getElementById("counter").innerText = amountOfCirclesOnScreen;
}, 40);

关键逻辑说明

  • 存储圆形数据:必须保存每个圆形的圆心坐标和半径,否则无法进行后续的碰撞计算。
  • 坐标转换:event.clientX/Y是相对于浏览器视口的坐标,需要减去Canvas的页面偏移量,才能得到Canvas内部的正确坐标。
  • 距离检测:使用勾股定理计算点击点到圆心的直线距离,若距离小于等于圆形半径,即判定为点击命中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 10:15:21