HTML Canvas drag and drop + snap functionality - javascript

I made this simple example of a drag-and-drop circle inside an HTML canvas. Here's the code below:
var c = document.getElementById('myCanvas');
var ctx = c.getContext('2d');
width = c.width = window.innerWidth * 0.9;
height = c.height = window.innerHeight * 0.9;
var handle = {
x: width / 2,
y: height / 2,
radius: 30,
};
function draw() {
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
ctx.arc(handle.x, handle.y, handle.radius, 0, Math.PI * 2, false);
ctx.fill();
ctx.stroke();
drawLines();
}
function drawLines() {
ctx.beginPath();
ctx.moveTo(0, height / 2);
ctx.lineTo(width, height / 2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(width / 6, 0);
ctx.lineTo(width / 6, height);
ctx.stroke();
}
function circlePointCollision(x, y, circle) {
return distanceXY(x, y, circle.x, circle.y) < circle.radius;
}
function distanceXY(x0, y0, x1, y1) {
var dx = x1 - x0,
dy = y1 - y0;
return Math.sqrt(dx * dx + dy * dy);
}
document.addEventListener('mousedown', function (e) {
if (circlePointCollision(e.x , e.y , handle)) {
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
}
});
function onMouseMove(e) {
handle.x = e.pageX;
handle.y = e.pageY;
draw();
}
function onMouseUp() {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
}
draw();
drawLines();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<canvas id="myCanvas" style="border: 1px solid black"></canvas>
<script src="./script.js"></script>
</body>
</html>
I created this with a help of some resources from this video and these are the materials they used. I'm trying to write a proof of concept for snap functionality. Basically, I want to make that circle "snap" in the middle of the crossing two lines when it's being moved across (or very close to). When it's snapped in the intersection, it should be a little bit harder to move it from there. Due to being relatively new to HTML canvas, I'm not exactly sure what the best approach is.
Can anyone help me to expand my snippet and make it do what I need?

Before assigning handle.x and handle.y in onMouseMove simply compare e.pageX and e.pageY to the intersection point and if they are close enough set the the handle coordinates to the intersection instead of e.pageX and e.pageY.
I've created local variables here as an example, but of course you would want to either have a snap function that maintained the threshold and a list of intersections (or a grid declaration of some sort) to check against or declare threshold and intersection globally and check against them.
function onMouseMove(e) {
const threshold = handle.radius * 0.8;
const intersection = { x: width / 6, y: height / 2 }
handle.x = distanceXY(e.pageX, e.pageY, intersection.x, intersection.y) < threshold ? intersection.x : e.pageX;
handle.y = distanceXY(e.pageX, e.pageY, intersection.x, intersection.y) < threshold ? intersection.y : e.pageY;
draw();
}
var c = document.getElementById('myCanvas');
var ctx = c.getContext('2d');
width = c.width = window.innerWidth * 0.9;
height = c.height = window.innerHeight * 0.9;
var handle = {
x: width / 2,
y: height / 2,
radius: 30,
};
function draw() {
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
ctx.arc(handle.x, handle.y, handle.radius, 0, Math.PI * 2, false);
ctx.fill();
ctx.stroke();
drawLines();
}
function drawLines() {
ctx.beginPath();
ctx.moveTo(0, height / 2);
ctx.lineTo(width, height / 2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(width / 6, 0);
ctx.lineTo(width / 6, height);
ctx.stroke();
}
function circlePointCollision(x, y, circle) {
return distanceXY(x, y, circle.x, circle.y) < circle.radius;
}
function distanceXY(x0, y0, x1, y1) {
var dx = x1 - x0,
dy = y1 - y0;
return Math.sqrt(dx * dx + dy * dy);
}
document.addEventListener('mousedown', function (e) {
if (circlePointCollision(e.x, e.y, handle)) {
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
}
});
function onMouseMove(e) {
const threshold = handle.radius * 0.8;
const intersection = { x: width / 6, y: height / 2 }
handle.x = distanceXY(e.pageX, e.pageY, intersection.x, intersection.y) < threshold ? intersection.x : e.pageX;
handle.y = distanceXY(e.pageX, e.pageY, intersection.x, intersection.y) < threshold ? intersection.y : e.pageY;
draw();
}
function onMouseUp() {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
}
draw();
drawLines();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<canvas id="myCanvas" style="border: 1px solid black"></canvas>
<script src="./script.js"></script>
</body>
</html>

Related

check if circle is hovered HTML5 Canvas [duplicate]

I'm new to canvas and I'm trying to listen to mouse clicks on the circles that I previously drew on canvas. I'm trying to change the colour of the circles (maybe to green) when I click on them.
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var radius = 15;
for (var i = 0; i < 600; i += 100) {
var x = 100 + i;
var y = 100;
draw_circle(i, x, y, radius);
}
function draw_circle(ID, x, y, radius) {
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2, false);
context.fillStyle = 'red';
context.fill();
context.lineWidth = 3;
context.strokeStyle = 'black';
context.stroke();
context.closePath();
}
<!DOCTYPE HTML>
<html>
<head>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="700" height="700"></canvas>
</body>
</html>
Just add the arc path again and use isPointInPath() with the mouse position. Only one arc can be tested at once, but it's fast to clear and add new paths. Do this in a loop.
Example
var c = document.querySelector("canvas"), ctx = c.getContext("2d");
getArc(50, 50, 40);
ctx.fill();
c.onclick = function(e) {
var rect = this.getBoundingClientRect(), // get abs. position of canvas
x = e.clientX - rect.left, // adjust mouse-position
y = e.clientY - rect.top;
getArc(50, 50, 40); // get path wo/ filling/stroking it
if (ctx.isPointInPath(x, y)) alert("Clicked");
};
function getArc(x, y, r) {
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI*2);
ctx.closePath();
}
<canvas></canvas>
The other approach is to use math and trigonometry:
// at this point we have the mouse position, so:
var dx = x - arcX,
dy = y - arcY,
dist = Math.abs(Math.sqrt(dx*dx + dy*dy));
if (dist <= radius) { ...clicked... };
Tip: you can skip squaring the dist by using r2 instead.
With SVG, it would be easy - a circle is an element, and can have a click handler, and has fill that you can manipulate to change colour.
With Canvas, you need to:
save the data for each circle you draw (center and radius)
capture click on canvas as cx, cy
check every circle data x, y, r you have, see whether dx * dx + dy * dy < r * r, where dx = cx - x, dy = cy - y. Circles that satisfy this equation were clicked
repaint the circle
With Canvas, you can use function addEventListener. There are quite a few mouse events you can detect: mousedown, mouseup, mousemove, mouseout and mouseover.
Here is a example:
<!DOCTYPE HTML>
<html>
<head>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0px;
}
</style>
<script>
function initialize() {
var canvas = document.getElementById("myCanvas");
canvas.addEventListener("mousedown", doMouseDown, false);
}
function doMouseDown() {
canvas_X = event.pageX;
canvas_Y = event.pageY;
alert("X = " + canvas_X + "Y = " + canvas_Y);
}
</script>
</head>
<body>
<canvas id="myCanvas" width="700" height="700"></canvas>
</body>
</html>

How to make a line follow mouse event arctanget with class javascript

Hi every one I'm trying to make a line follow the mouse move event. I can do this with the ctx.save and restore but i don't know how to do if i want use class and draw other object. In fact whit using save and restore It Work but if i want to have the same result whit class i dont know how to do many thanks
window.onload = function() {
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
width = canvas.width = window.innerWidth,
height = canvas.height = window.innerHeight,
arrowX = width / 2,
arrowY = height / 2,
dx, dy,
angle = 0,
a = 0;
render();
function render() {
arrowX = width / 2 + Math.cos(a) * height * .4;
arrowY = height / 2 + Math.sin(a) * height * .4;
context.clearRect(0, 0, width, height);
context.save();
context.translate(arrowX, arrowY);
context.rotate(angle);
context.beginPath();
context.moveTo(20, 0);
context.lineTo(-20, 0);
context.moveTo(20, 0);
context.lineTo(10, -10);
context.moveTo(20, 0);
context.lineTo(10, 10);
context.stroke();
context.restore();
requestAnimationFrame(render);
}
document.body.addEventListener("mousemove", function(event) {
dx = event.clientX - arrowX;
dy = event.clientY - arrowY;
angle = Math.atan2(dy, dx);
});
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body style="height: 100vh;">
<Canvas id="canvas"></Canvas>
<script src="main.js"></script>
</body>
</html>
If i want use class i start like thaïs on the following code
var canvas = document.getElementById("canvas");
var c = canvas.getContext("2d");
var width = canvas.width = window.innerWidth;
var height = canvas.height = window.innerHeight;
var arrowX = width / 2;
var arrowY = height -20;
var dx;
var dy;
var angle = 0;
class Player{
constructor(x,y,radius,color)
{
this.x=x;
this.y=y;
this.radius=radius;
this.color=color;
}
draw(){
c.beginPath();
c.arc(this.x,this.y,this.radius, 0, Math.PI *2,false );
c.fillStyle=this.color;
c.fill();
}
}
class Shooter{
constructor(x,y,xEnd,yEnd,color){
this.x=x;
this.y=y;
this.xEnd=xEnd;
this.yEnd=yEnd;
this.color=color;
}
draw(){
c.beginPath();
c.moveTo(this.x, this.y);
c.lineTo(this.xEnd, this.yEnd);
c.strokeStyle=this.color;
c.stroke();
}
}
// const player= new Player(arrowX,arrowY,20,'white');
const shooter= new Shooter(arrowX,500,arrowX,arrowY,'white');
let animationId;
function animate(){
animationId=requestAnimationFrame(animate)
c.fillStyle='rgba(0, 0, 0, 0.5)';
c.fillRect(0,0,canvas.width,canvas.height);
//player.draw();
shooter.draw();
}
requestAnimationFrame(animate);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body style="height: 100vh;">
<Canvas id="canvas"></Canvas>
<script src="main.js"></script>
</body>
</html>
I can offer two methods for this. The first is to simply have the end of the vector the same coordinates as the mouse position. This will change the magnitude of the vector of course.
This method does not rotate or translate the context.
var canvas = document.getElementById("canvas");
var c = canvas.getContext("2d");
var width = (canvas.width = window.innerWidth);
var height = (canvas.height = window.innerHeight);
let canvasBounds = canvas.getBoundingClientRect()
let mouse = {x: 0, y: 0}
class Shooter {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.xEnd = mouse.x;
this.yEnd = mouse.y;
this.color = color;
}
draw() {
c.beginPath();
c.moveTo(this.x, this.y);
c.lineTo(this.xEnd, this.yEnd);
c.strokeStyle = this.color;
c.stroke();
}
update() {
this.xEnd = mouse.x;
this.yEnd = mouse.y;
}
}
const shooter = new Shooter(width/2, height/2, "white");
canvas.addEventListener("mousemove", e => {
mouse.x = e.x - canvasBounds.x;
mouse.y = e.y - canvasBounds.y;
});
function animate() {
animationId = requestAnimationFrame(animate);
c.fillStyle = "rgba(0, 0, 0, 0.5)";
c.fillRect(0, 0, canvas.width, canvas.height);
//player.draw();
shooter.draw();
shooter.update();
}
requestAnimationFrame(animate);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body style="height: 100vh;">
<Canvas id="canvas"></Canvas>
<script src="main.js"></script>
</body>
</html>
The second method draws the start point at (0, 0) but then you can use translate to position it where ever you want. This also rotates the context but allows you to keeps the magnitude the same.
var canvas = document.getElementById("canvas");
var c = canvas.getContext("2d");
var width = (canvas.width = window.innerWidth);
var height = (canvas.height = window.innerHeight);
let canvasBounds = canvas.getBoundingClientRect()
let mouse = {x: 0, y: 0}
class Shooter {
constructor(x, y, color) {
this.x = 0;
this.y = 0;
this.xEnd = this.x + 50;
this.yEnd = this.y;
this.translate = {x: x, y: y}
this.color = color;
this.angle = 0;
}
draw() {
this.angle = Math.atan2(mouse.y - this.translate.y, mouse.x - this.translate.x)
c.save();
c.translate(this.translate.x, this.translate.y);
c.rotate(this.angle);
c.beginPath();
c.moveTo(this.x, this.y);
c.lineTo(this.xEnd, this.yEnd);
c.strokeStyle = this.color;
c.stroke();
c.restore()
}
update() {
}
}
const shooter = new Shooter(width/2, height/2, "white");
canvas.addEventListener("mousemove", e => {
mouse.x = e.x - canvasBounds.x;
mouse.y = e.y - canvasBounds.y;
});
function animate() {
requestAnimationFrame(animate);
c.fillStyle = "rgba(0, 0, 0, 0.5)";
c.fillRect(0, 0, canvas.width, canvas.height);
//player.draw();
shooter.draw();
}
requestAnimationFrame(animate);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body style="height: 100vh;">
<Canvas id="canvas"></Canvas>
<script src="main.js"></script>
</body>
</html>

How to translate and make canvas responsive?

I am trying to move to translate the canvas to (100,100) but it is not moving.
And the canvas is not responsive, when I try to change the size of canvas using CSS the circles are shrinking.
How can I add a button to toggle the drawing on canvas by the circle (i.e.: when button pressed don't draw then the circle should not draw on canvas, when pressed draw it should draw)
requestAnimationFrame(animate);
var ctx = canvas1.getContext('2d');
ctx.translate(100,100);
canvas1.width = innerWidth;
canvas1.height = innerHeight;
const bgCan = copyCanvas(canvas1);
const redSize = 6, blueSize = 5; // circle sizes on pixels
const drawSpeed = 1; // when button down draw speed in pixels per frame
var X = 50, Y = 50;
var angle = 0;
var mouseButtonDown = false;
document.addEventListener('mousedown', () => mouseButtonDown = true);
document.addEventListener('mouseup', () => mouseButtonDown = false);
function copyCanvas(canvas) {
const can = Object.assign(document.createElement("canvas"), {
width: canvas.width, height: canvas.height
});
can.ctx = can.getContext("2d");
return can;
}
function circle(ctx){
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(X, Y, redSize, 0, Math.PI*2);
ctx.fill();
}
function direction(ctx){
const d = blueSize + redSize + 3;
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(d * Math.sin(angle) + X, d * Math.cos(angle) + Y, blueSize, 0, Math.PI*2);
ctx.fill();
}
function animate(){
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(bgCan, 0, 0);
if (mouseButtonDown) {
circle(bgCan.ctx);
X += Math.sin(angle) * drawSpeed;
Y += Math.cos(angle) * drawSpeed;
} else {
angle += 0.1;
circle(ctx);
}
direction(ctx);
requestAnimationFrame(animate);
}
#canvas1{
position: absolute;
top:0;
left: 0;
width: 100%;
height: 100%;
border-width: 1px;
border-style: solid;
border-color: Black;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas basics</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas1"></canvas>
<input onclick="change()" type="button" value="Write" id="mybutton1"></input>
<script src="script.js"></script>
</body>
</html>
the translate(100, 100) gets reset by new width/height of the canvas. It just needs to be executed after width/height is set
The easiest way is to have a window resize listener and update canvas style width/height
I'm not sure why, but for some reason canvas has a higher z-index therefor button's onclick() never fired
In the code below I've added boundaries restriction, it's affected by translate(100, 100).
requestAnimationFrame(animate);
var ctx = canvas1.getContext('2d');
canvas1.width = innerWidth;
canvas1.height = innerHeight;
ctx.translate(100, 100);
const bgCan = copyCanvas(canvas1);
const redSize = 6,
blueSize = 5; // circle sizes on pixels
const drawSpeed = 1; // when button down draw speed in pixels per frame
const drawButton = document.getElementById("mybutton1");
var draw = true;
var X = 50,
Y = 50;
var angle = 0;
var mouseButtonDown = false;
document.addEventListener('mousedown', onMouseEvent);
document.addEventListener('mouseup', onMouseEvent);
function copyCanvas(canvas) {
const can = Object.assign(document.createElement("canvas"), {
width: canvas.width,
height: canvas.height
});
can.ctx = can.getContext("2d");
return can;
}
function circle(ctx, mousedown) {
if (mousedown && !draw)
return;
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(X, Y, redSize, 0, Math.PI * 2);
ctx.fill();
}
function direction(ctx) {
const d = blueSize + redSize + 3;
ctx.fillStyle = draw ? 'blue' : 'red';
ctx.beginPath();
ctx.arc(d * Math.sin(angle) + X, d * Math.cos(angle) + Y, blueSize, 0, Math.PI * 2);
ctx.fill();
}
function animate() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(bgCan, 0, 0);
if (mouseButtonDown) {
circle(bgCan.ctx, mouseButtonDown);
const x = X + Math.sin(angle) * drawSpeed,
y = Y + Math.cos(angle) * drawSpeed;
if (x > (blueSize + redSize) * 2 && x < canvas1.width - (blueSize + redSize) * 2 &&
y > (blueSize + redSize) * 2 && y < canvas1.height - (blueSize + redSize) * 2)
{
X = x;
Y = y;
}
} else {
angle += 0.1;
circle(ctx);
}
direction(ctx);
requestAnimationFrame(animate);
}
function onMouseEvent(e) {
if (e.target === drawButton) {
if (e.type == "mouseup")
{
draw = !draw;
document.getElementById("mybutton1").value = draw ? "Write" : "Move";
}
return;
}
mouseButtonDown = e.type == "mousedown";
}
window.addEventListener("resize", handleResize)
function handleResize ()
{
const ratio = canvas1.width / canvas1.height;
let h = window.innerHeight,
w = h * ratio;
if (w > window.innerWidth) {
w = window.innerWidth;
h = w / ratio;
}
canvas1.style.width = w + 'px';
canvas1.style.height = h + 'px';
};
handleResize(); // First draw
#canvas1 {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-width: 1px;
border-style: solid;
border-color: Black;
z-index: -1; /* let other elements receive events */
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas basics</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas1" scaleMode='fill'></canvas>
<input type="button" value="Write" id="mybutton1" />
<script src="script.js"></script>
</body>
</html>

How can I make two circle objects (inside the canvas ) movable?

I have one circle that movable in canvas, and the circle is not the set position. It is created in any place when the mouse is clicked. I am trying to create two circles (pink and yellow) in a set position, and I want to create the circles drag-gable (mouse click -> able to move X, Y positions ) to any places on the canvas. How can I attempt this?
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
canvas.addEventListener('mousedown', function(e) {
this.down = true;
this.X = e.pageX;
this.Y = e.pageY;
}, 0);
canvas.addEventListener('mouseup', function() {
this.down = false;
}, 0);
canvas.addEventListener('mousemove', function(e) {
if (this.down) {
// clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(this.X, this.Y, 50, 0, 2 * Math.PI, true);
ctx.fillStyle = "#FF6A6A";
ctx.fill();
ctx.stroke();
this.X = e.pageX;
this.Y = e.pageY;
}
}, 0);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Canvas</title>
</head>
<body>
<canvas id="canvas" style='background-color:#EEE;' width='500px' height='200px'></canvas>
</body>
</html>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var model = {
circle1: { x: 200, y: 200 },
circle2: { x: 200, y: 200 }
};
var radius = 50;
function view(ctx, model) {
function circle(c) {
ctx.beginPath();
ctx.beginPath();
ctx.arc(c.x, c.y, radius, 0, 2 * Math.PI, true);
ctx.fillStyle = "#FF6A6A";
ctx.fill();
ctx.stroke();
}
// clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
circle(model.circle1);
circle(model.circle2);
}
function redraw() {
view(ctx, model);
}
redraw();
function getCircleForPosition(x, y) {
function intersect(a, b) {
var d2 = Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2);
r2 = Math.pow(radius, 2);
return d2 < r2;
}
return [model.circle1, model.circle2].find(circle => intersect(circle, { x, y }));
}
canvas.addEventListener('mousedown', function(e) {
model.dragging = getCircleForPosition(e.pageX, e.pageY);
}, 0);
canvas.addEventListener('mouseup', function() {
model.dragging = undefined;
}, 0);
canvas.addEventListener('mousemove', function(e) {
if (model.dragging) {
model.dragging.x = e.pageX;
model.dragging.y = e.pageY;
redraw();
}
}, 0);
<canvas id="canvas" style='background-color:#EEE;' width='500px' height='500px'></canvas>
fiddle: https://jsfiddle.net/eguneys/qgwtaL2p/18/

Detect mouse clicks on circles on HTML5 canvas

I'm new to canvas and I'm trying to listen to mouse clicks on the circles that I previously drew on canvas. I'm trying to change the colour of the circles (maybe to green) when I click on them.
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var radius = 15;
for (var i = 0; i < 600; i += 100) {
var x = 100 + i;
var y = 100;
draw_circle(i, x, y, radius);
}
function draw_circle(ID, x, y, radius) {
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2, false);
context.fillStyle = 'red';
context.fill();
context.lineWidth = 3;
context.strokeStyle = 'black';
context.stroke();
context.closePath();
}
<!DOCTYPE HTML>
<html>
<head>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="700" height="700"></canvas>
</body>
</html>
Just add the arc path again and use isPointInPath() with the mouse position. Only one arc can be tested at once, but it's fast to clear and add new paths. Do this in a loop.
Example
var c = document.querySelector("canvas"), ctx = c.getContext("2d");
getArc(50, 50, 40);
ctx.fill();
c.onclick = function(e) {
var rect = this.getBoundingClientRect(), // get abs. position of canvas
x = e.clientX - rect.left, // adjust mouse-position
y = e.clientY - rect.top;
getArc(50, 50, 40); // get path wo/ filling/stroking it
if (ctx.isPointInPath(x, y)) alert("Clicked");
};
function getArc(x, y, r) {
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI*2);
ctx.closePath();
}
<canvas></canvas>
The other approach is to use math and trigonometry:
// at this point we have the mouse position, so:
var dx = x - arcX,
dy = y - arcY,
dist = Math.abs(Math.sqrt(dx*dx + dy*dy));
if (dist <= radius) { ...clicked... };
Tip: you can skip squaring the dist by using r2 instead.
With SVG, it would be easy - a circle is an element, and can have a click handler, and has fill that you can manipulate to change colour.
With Canvas, you need to:
save the data for each circle you draw (center and radius)
capture click on canvas as cx, cy
check every circle data x, y, r you have, see whether dx * dx + dy * dy < r * r, where dx = cx - x, dy = cy - y. Circles that satisfy this equation were clicked
repaint the circle
With Canvas, you can use function addEventListener. There are quite a few mouse events you can detect: mousedown, mouseup, mousemove, mouseout and mouseover.
Here is a example:
<!DOCTYPE HTML>
<html>
<head>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0px;
}
</style>
<script>
function initialize() {
var canvas = document.getElementById("myCanvas");
canvas.addEventListener("mousedown", doMouseDown, false);
}
function doMouseDown() {
canvas_X = event.pageX;
canvas_Y = event.pageY;
alert("X = " + canvas_X + "Y = " + canvas_Y);
}
</script>
</head>
<body>
<canvas id="myCanvas" width="700" height="700"></canvas>
</body>
</html>

Categories

Resources