Handling Mouse Input With JavaScript

mice

This is the eighth article of an ongoing series about game development for the Web.

In the previous article, we saw a practical demonstration of event-driven development in JavaScript.

We used the available KeyEvents in the DOM Level 2 Event Model to handle user input with the keyboard. Furthermore, we talked about specific scenarios like game input and text input and what are the most suitable events to handle each.

In this delivery, we will extend the previous article by talking about the events used to handle mouse input.


Mouse Input

Every time a pointing device interacts with the browser window an event is triggered.

Unlike keyboards, pointing devices provide a wide range of interactivity. As of the writing of this article, interactions between a pointing device and a browser window with a rendered HTML document can be tracked using ten events:

  • mousedown: A pointing device button (usually a mouse) is pressed on an element.
  • mouseenter: A pointing device is moved onto the element that has the listener attached.
  • mouseleave: A pointing device is moved off the element that has the listener attached.
  • mousemove: A pointing device is moved over an element.
  • mouseout: A pointing device is moved off the element that has the listener attached or off one of its children.
  • mouseover: A pointing device is moved onto the element that has the listener attached or onto one of its children.
  • mouseup: A pointing device button is released over an element.
  • click: A pointing device button has been pressed and released on an element.
  • dblclick: A pointing device button is clicked twice on an element.
  • contextmenu: The right button of the mouse is clicked (before the context menu is displayed).

The registered event handlers receive a subclass of the Event object called MouseEvent with relevant information about the activities performed with a pointing device.

Every mouse event contains two properties to determine the current location of the mouse cursor. Prior to the standardization of the MouseEvent interface, properties that represented mouse coordinates varied across Web Browsers.

Example:

canvas.addEventListener('click', function(event){
    // Capture the Window X & Y coordinates of the mouse cursor
    //
    var x = event.clientX;
    var y = event.clientY;
}, false);
Click anywhere on the screen to retrieve the mouse cursor coordinates:


Screen-to-canvas space convertion

As previously stated, the MouseEvent object provides pointer coordinates through the clientX and clientY properties of its interface. These coordinates are relative to the browser window. Most of the time we need to know where the mouse events occur relative to the <canvas> element instead of the window. This means that the pointer coordinates need to be translated from "Window Space" → "Canvas Space".

The transformation of the coordinates can be achieved in different ways:

1. Using the HTMLElement API

The HTMLElement interface represents any HTML element. This interface exposes two properties that will help with the coordinate system transformation: offsetLeft & offsetTop.

Example:

canvas.on('click', function(event) {

    // use clientX and clientY to get the mouse position
    // relative to the browser window

    var mouse = {
        x: event.clientX - canvas.offsetLeft,
        y: event.clientY - canvas.offsetTop
    }

    // now you have local coordinates,
    // which consider a (0,0) origin at the
    // top-left of canvas element
});

2. Using the Element API

The Element interface represents an object within a DOM document. This interface exposes a method that will help with the coordinate system transformation: getBoundingClientRect().

Example:

canvas.on('click', function(event) {

    // Get the bounding rect
    //
    var bounds = canvas.getBoundingClientRect();

    // use clientX and clientY to get the mouse position
    // relative to the browser window

    var mouse = {
        x: event.clientX - bounds.left,
        y: event.clientY - bounds.top
    }

    // now you have local coordinates,
    // which consider a (0,0) origin at the
    // top-left of canvas element
});

The Result

Both approaches provide the same result.


Let's give it purpose

Let's make a tool that enables us to create doodles. This will help us understand how to combine different mouse events in games and other interactive applications that rely on the <canvas> element.

1. Let's keep track of the mouse pointer path

The main focus of this step is to:

  • Track the pointer just when it's inside the canvas and draw a path with its trajectory.

Example:

var started = false;

// Get a reference to the drawing context 
// 
var ctx = canvas.getContext('2d');

function onMouseEvent (event) {
      var x, y;

    // Transform the pointer coordinates to canvas space
    //
    ...

    // The event handler works like a drawing pencil which tracks the mouse 
    // movements. We start drawing a path made up of lines.
    // 
    if (!started) {
        ctx.beginPath();
        ctx.moveTo(x, y);
        started = true;
    } else {
        ctx.lineTo(x, y);
        ctx.stroke();
    }
}

canvas.addEventListener('mousemove', onMouseEvent, false);
To draw: move the mouse above the canvas.


2. Let's keep track of the mouse pointer path only when the left click is hold by the user

The main focus of this step is to:

  • Make the user left click and hold button the to draw.

Example:

// This painting tool works like a drawing pencil which tracks the mouse 
// movements.
function tool() {

    var tool = this;
    this.started = false;

    // This is called when you start holding down the mouse button.
    // This starts the pencil drawing.
    this.mousedown = function (event) {
       ctx.beginPath();
       ctx.moveTo(event._x, event._y);
       tool.started = true;
    };

    // This function is called every time you move the mouse. Obviously, it only 
    // draws if the tool.started state is set to true (when you are holding down 
    // the mouse button).
    this.mousemove = function (event) {                                        
        if (tool.started) {                        
            ctx.lineTo(event._x, event._y);
            ctx.stroke();
        }
    };

    // This is called when you release the mouse button.
    this.mouseup = function (event) {                    
        if (tool.started) {                        
            tool.mousemove(event);
            tool.started = false;
        }
    };
}
To draw: left click and hold then move the mouse above the canvas.


3. Let's add a color palette

The main focus of this step is to:

  • Allow the user to select different colors.

Example:

// Current color
//
var currentColor = "black";

// Color palette options
//
var colors = {};            
colors["black"] = "#000000";
colors["red"] = "#FF0000";
colors["yellow"] = "#FFFF00";
colors["green"] = "#00FF00";
colors["blue"] = "#0000FF";

// Get a reference to the drawing context 
// 
var ctx = canvas.getContext('2d');

// Set the current color
//
ctx.strokeStyle = currentColor;

// Event for all palette options
//
function onClickEvent(event){
    // Palette color selected
    //
    var selectedColor = event.srcElement.id;                

    // Selected color label
    //
    displayColor.innerHTML = selectedColor;

    // Set the current color
    //
    currentColor = colors[selectedColor];                
}
To draw: select a color, then left click and hold then move the mouse above the canvas.
Select a color: black



Code Examples

Working examples of these articles can be downloaded through the following link. The source code for these and future examples are hosted in a public repository at Github.

Feel free to fork and submit pull requests.


Conclusion

In this article, we discussed how JavaScript events help us handle user input through the mouse. We talked about how to create MouseEvent listeners for the <canvas> element and evaluated the different available options and when to use each.

Have you ever implemented a mouse input system in JavaScript? If so, what considerations have you made and what advice would you give?


References

Image source: CapeTownGuy

Drowell, E. (n.d.). HTML5 Canvas Mouse Coordinates Tutorial. Retrieved from HTML5 Canvas Tutorials: http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/

Moot, K. (2012, July 24). Handling user input in HTML5 Canvas-based games. Retrieved from developerWorks: http://www.ibm.com/developerworks/library/wa-games/index.html