Working with mouse events requires detecting whether a mouse button is up or down, identifying that button, keeping track of mouse movement, getting the x and y coordinates of the cursor, etc.
Special care must be taken when acquiring mouse coordinates because the HTML5 canvas has default (or directed) CSS properties which could produce false coordinates. The trick to get the right x and y mouse cursor coordinates is to use this method from the canvas API:
// necessary to take into account CSS boudaries
var rect = canvas.getBoundingClientRect();
The width and height of the rect object must be taken into account. These dimensions correspond to the padding / margins / borders of the canvas. See how we deal with them in the getMousePos() function in the next example.
Check an online example below that covers all cases correctly.
Move the mouse over the canvas and press or release mouse buttons. Notice that we keep the state of the mouse (position, buttons up or down) as part of the inputStates object, just as we do with the keyboard (per previous lesson).
Below is the JavaScript source code for this small example:
var canvas, ctx;
var inputStates = {};
window.onload = function init() {
canvas = document.getElementById('myCanvas');
ctx = canvas.getContext('2d');
canvas.addEventListener('mousemove', function (evt) {
inputStates.mousePos = getMousePos(canvas, evt);
var message = 'Mouse position: ' + inputStates.mousePos.x + ',' + inputStates.mousePos.y;
writeMessage(canvas, message);
}, false);
canvas.addEventListener('mousedown', function (evt) {
inputStates.mousedown = true;
inputStates.mouseButton = evt.button;
var message = "Mouse button " + evt.button + " down at position: " +
inputStates.mousePos.x + ',' + inputStates.mousePos.y;
writeMessage(canvas, message);
}, false);
canvas.addEventListener('mouseup', function (evt) {
inputStates.mousedown = false;
var message = "Mouse up at position: " + inputStates.mousePos.x + ',' +
inputStates.mousePos.y;
writeMessage(canvas, message);
}, false);
};
function writeMessage(canvas, message) {
var ctx = canvas.getContext('2d');
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = '18pt Calibri';
ctx.fillStyle = 'black';
ctx.fillText(message, 10, 25);
ctx.restore();
}
function getMousePos(canvas, evt) {
// necessary to take into account CSS boudaries
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
Source code:
var canvas, ctx, width, height;
var rect = {x:40, y:40, rayon: 30, width:80, height:80, v:1};
var mousepos = {x:0, y:0};
function init() {
canvas = document.querySelector("#myCanvas");
ctx = canvas.getContext('2d');
width = canvas.width;
height = canvas.height;
canvas.addEventListener('mousemove', function (evt) {
mousepos = getMousePos(canvas, evt);
}, false);
mainloop();
}
function mainloop() {
// 1) clear screen
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 2) move object
var dx = rect.x - mousepos.x;
var dy = rect.y - mousepos.y;
var angle = Math.atan2(dy, dx);
rect.x -= rect.v*Math.cos(angle);
rect.y -= rect.v*Math.sin(angle);
// 3) draw object
drawRectangle(angle);
// request new frame
window.requestAnimationFrame(mainloop);
}
function drawRectangle(angle) {
ctx.save();
// These two lines move the coordinate system
ctx.translate(rect.x, rect.y);
ctx.rotate(angle);
// recenter the coordinate system in the middle
// the rectangle. Like that it will rotate around
// this point instead of top left corner
ctx.translate(-rect.width/2, -rect.height/2);
ctx.fillRect(0, 0, rect.width, rect.height);
ctx.restore();
}
function getMousePos(canvas, evt) {
// necessary to take into account CSS boudaries
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
Line 25 calculates the angle between mouse cursor and the rectangle,
Lines 27-28 move the rectangle v pixels along a line between the rectangle's current position and the mouse cursor,
Lines 41-46 translate the rectangle, rotate it, and recenter the rotational point to the center of the rectangle (in its new position).
Now we will include these listeners into our game framework. Notice that we changed some parameters (no need to pass the canvas as a parameter of the getMousePos() function, for example).
Try pressing arrows and space keys, moving the mouse, and pressing the buttons, all at the same time. You'll see that the game framework handles all these events simultaneously because the global variable named inputStates is updated by keyboard and mouse events, and consulted to direct movements every 1/60th second.