40

How can I detect when the user clicks inside the red bubble?

It should not be like a square field. The mouse must be really inside the circle:

img

Here's the code:

<canvas id="canvas" width="1000" height="500"></canvas>
<script>
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d")

var w = canvas.width
var h = canvas.height

var bubble = {
  x: w / 2,
  y: h / 2,
  r: 30,
}

window.onmousedown = function(e) {
    x = e.pageX - canvas.getBoundingClientRect().left
    y = e.pageY - canvas.getBoundingClientRect().top

    if (MOUSE IS INSIDE BUBBLE) {
        alert("HELLO!")
    }
}

ctx.beginPath()
ctx.fillStyle = "red"
ctx.arc(bubble.x, bubble.y, bubble.r, 0, Math.PI*2, false)
ctx.fill()
ctx.closePath()
</script>
gre_gor
  • 6,669
  • 9
  • 47
  • 52
  • 1
    If you know the position of the cursor and the position and size of the circle (and it really is a circle, not an ellipsis), it's just a simple geometric calculation to determine if the point (the cursor) is within the circle or not. http://stackoverflow.com/questions/481144/equation-for-testing-if-a-point-is-inside-a-circle – qJake May 28 '13 at 13:07
  • Heard of sinus and cosinus …? – CBroe May 28 '13 at 13:08
  • 1
    @CBroe Trig is not needed in this case, nor would it be useful. – Benjamin Gruenbaum May 28 '13 at 13:11
  • Not sure why this is being upvoted. It shows no research effort and has already been asked *multiple* times. Voting to close. – arkon Jul 17 '16 at 20:57

4 Answers4

61

A circle, is the geometric position of all the points whose distance from a central point is equal to some number "R".

You want to find the points whose distance is less than or equal to that "R", our radius.

The distance equation in 2d euclidean space is d(p1,p2) = root((p1.x-p2.x)^2 + (p1.y-p2.y)^2).

Check if the distance between your p and the center of the circle is less than the radius.

Let's say I have a circle with radius r and center at position (x0,y0) and a point (x1,y1) and I want to check if that point is in the circle or not.

I'd need to check if d((x0,y0),(x1,y1)) < r which translates to:

Math.sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)) < r

In JavaScript.

Now you know all these values (x0,y0) being bubble.x and bubble.y and (x1,y1) being x and y.

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • p1 is the center of your circle (which you calculated already by doing width/2 and height/2). p2 is the current mouse position you're checking against the circle. – Benjamin Gruenbaum May 28 '13 at 13:10
44

To test if a point is within a circle, you want to determine if the distance between the given point and the center of the circle is smaller than the radius of the circle.

Instead of using the point-distance formula, which involves the use of a (slow) square root, you can compare the non-square-rooted (or still-squared) distance between the points. If that distance is less than the radius squared, then you're in!

// x,y is the point to test
// cx, cy is circle center, and radius is circle radius
function pointInCircle(x, y, cx, cy, radius) {
  var distancesquared = (x - cx) * (x - cx) + (y - cy) * (y - cy);
  return distancesquared <= radius * radius;
}

(Not using your code because I want to keep the function general for onlookers who come to this question later)

This is slightly more complicated to comprehend, but its also faster, and if you intend on ever checking point-in-circle in a drawing/animation/object moving loop, then you'll want to do it the fastest way possible.

Related JS perf test:

http://jsperf.com/no-square-root

Simon Sarris
  • 62,212
  • 13
  • 141
  • 171
  • 1
    Thanks for making such readable and re-usable code. Just a note on the performance test, in modern browsers, it seems using square root is ever so slightly faster. – pedalpete Feb 17 '17 at 05:58
6

Just calculate the distance between the mouse pointer and the center of your circle, then decide whether it's inside:

var dx = x - bubble.x,
dy = y - bubble.y,
dist = Math.sqrt(dx * dx + dy * dy);

if (dist < bubble.r) {
  alert('hello');
}

Demo

As mentioned in the comments, to eliminate Math.sqrt() you can use:

var distsq = dx * dx + dy * dy,
rsq = bubble.r * bubble.r;

if (distsq < rsq) {
   alert('HELLO');
}
Community
  • 1
  • 1
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
  • 5
    You can even cut out the "expensive" Math.sqrt by testing like this: dx*dx+dy*dy < bubble.r*bubble.r – markE May 28 '13 at 16:32
4

An alternative (not always useful meaning it will only work for the last path (re)defined, but I bring it up as an option):

x = e.pageX - canvas.getBoundingClientRect().left
y = e.pageY - canvas.getBoundingClientRect().top

if (ctx.isPointInPath(x, y)) {
    alert("HELLO!")
}

Path can btw. be any shape.

For more details:
http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath

  • +1 for an answer that encompasses any path. Be sure to mention that the path to be tested must be defined (or redefined) just prior to hit-testing with isPointInPath. – markE May 28 '13 at 16:30