Working on canvas: Hit-testing circles (HTML and JavaScript)


In the previous working on canvas post, I explored creating and hit-testing rectangles using the canvas object and JavaScript. This time I'm going to add circles.


 

Click on the above shapes, get really close to the edges. See if you can catch the hit-test out.

Step 1: Circle drawing function

First of all it is necessary to add a circle drawing function to the code.
function drawCircle(x,y,r,color) {
context.beginPath();
// begins new path
context.arc(x,y,r,0,2*Math.PI);
context.fillStyle=color;
context.fill();
// use of fill autocloses a path
}

Step 2: Expand switch statement

Next it is necessary to extend the switch statement for the circle:
// draw object based on type
switch(objType) {
case "rectangle":
drawRect(objDimensions[0],objDimensions[1],objDimensions[2],objDimensions[3],color);
break;
case "circle":
drawCircle(objDimensions[0],objDimensions[1],objDimensions[2],color);
break;
default:
console.log("undefined");
}

Step 3: Hit-test extended

The hit-test must also be extended to receive the call from the switch statement. The maths here tests the distance of the touch from the origin of the circle, if it is less than the radius it's a hit.
// TEST FOR CIRCLE BEGINS
else if (objType == "circle") {
var a = Math.pow(x-objDimensions[0],2);
var b = Math.pow(y-objDimensions[1],2);
if (Math.sqrt(a+b)<=objDimensions[2]) {
document.getElementById("result").innerHTML="You hit: " + obj["id"];
// break once object has been found
break;
}
}
// TEST FOR CIRCLE ENDS

Step 4: Putting it all together

And here's the entire code necessary to replicate the above example:
<canvas height="200px" id="square" onclick="hitTest(event)" style="-webkit-tap-highlight-color: rgba(0,0,0,0); border: dotted 1px;" width="200px"></canvas>
<div id="result">
</div>
<script>

var objectArray = [{"rectangle":[50,50,100,150], "color":"lightgreen", "id":"green rectangle"},{"rectangle":[100,150,100,50], "color":"pink", "id":"pink rectangle"},{"circle":[100,150,60,50], "color":"lightblue", "id":"blue circle"},{"circle":[10,150,60,50], "color":"red", "id":"red circle"}];
// array of objects

// retrieve canvas and context
var canv = document.getElementById("square");
var context = canv.getContext("2d");

// rectangle drawing function
function drawRect(x,y,w,h,color) {

context.fillStyle=color;
context.fillRect(x,y,w,h);

}

// circle drawing function
function drawCircle(x,y,r,color) {
context.beginPath();
context.arc(x,y,r,0,2*Math.PI);
context.fillStyle=color;
context.fill();
// use of fill autocloses a path (see Mozilla Dev)
}

// retrieve object properties
for (i=0;i<=objectArray.length-1; i++) {
var keys = Object.keys(objectArray[i]);
var objType = keys[0];
var objDimensions = objectArray[i][objType];
var color = objectArray[i]["color"];


// draw objects based on type
switch(objType) {
case "rectangle":
drawRect(objDimensions[0],objDimensions[1],objDimensions[2],objDimensions[3],color);
break;

case "circle":
drawCircle(objDimensions[0],objDimensions[1],objDimensions[2],color);
break;

default:
console.log("undefined");
break;
}
}


function hitTest(e)
{

// first retrieve event target (i.e. canvas) - this could be tested for equality to canvas if multiple canvases exist
var object = e.target;
// get test position of hit relative to what's currently in the browser and where the canvas is positioned on the page
var rectObject = object.getBoundingClientRect();
var x=e.clientX - rectObject.left;
var y=e.clientY - rectObject.top;

// detect object clicked, starting with last in array because this will be uppermost
for (i=objectArray.length-1; i>=0; i--) {
var obj = objectArray[i];
var keys = Object.keys(objectArray[i]);
var objType = keys[0];
var objDimensions = obj[objType];
// test for rectangle

// TEST FOR RECTANGLE BEGINS
if (objType == "rectangle") {
if (x >= objDimensions[0] && x <= objDimensions[2] + objDimensions[0] && y >= objDimensions[1] && y <= objDimensions[3] + objDimensions[1]) {
document.getElementById("result").innerHTML="You hit: " + obj["id"];
// break once object has been found
break;}
}
// TEST FOR RECTANGLE ENDS

// TEST FOR CIRCLE BEGINS
else if (objType == "circle") {
var a = Math.pow(x-objDimensions[0],2);
var b = Math.pow(y-objDimensions[1],2);
if (Math.sqrt(a+b)<=objDimensions[2]) {
document.getElementById("result").innerHTML="You hit: " + obj["id"];
// break once object has been found
break;
}
}
// TEST FOR CIRCLE ENDS

document.getElementById("result").innerHTML="Co-ordinates: "+x+", "+y;
}
}
</script>

Conclusion

Now the hit-testing of circles and rectangles has been tackled, I want to move on to using them to create something functional in the next post. Stay tuned.

Endorse on Coderwall

Comments