Canvas Events

It is possible to detect mouse and keyboard events for a canvas (or any other HTML element).

Mouse Events

There are seven mouse events:

click
The event occurs when the mouse is clicked on the canvas.
dblclick
The event occurs when the mouse is double-clicked on the canvas.
mousemove
The event occurs when the mouse is moved while it is on the canvas.
mouseover
The event occurs when the mouse is moved onto the canvas.
mouseout
The event occurs when the mouse is moved out of the canvas.
mousedown
The event occurs when any mouse button is pressed on the canvas.
mouseup
The event occurs when any mouse button is released on the canvas.
wheel
The event occurs when the mouse wheel is used on the canvas.

We need to insert two pieces of code in order to use a mouse event.

Firstly, we need to associate the event function with the canvas. This is done by adding an event listener to the canvas.

canvas.addEventListener('click', clickHandler)

Secondly, we need to write the code that will be called when the event happens. This is just a javascript function, as shown below.

function clickHandler(e) 
{
   /* Do something */
}

The variable e is provided by the system. It provides access to the x and y location of the screen pixel that was clicked. To get the canvas pixel location, we need to subtract the canvas top left corner x and y value from the screen x and y. The example below gets the correct canvas x and y location.

Example showing how to get the canvas x and y position (Run Example)

<!DOCTYPE html>
<html>
    <head>
        <title>Course notes example code</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <style>
            #keyboardCanvas
            {
                border:1px solid black;
                margin-left: 100px;
                width:500px;
                height:500px;
                cursor:default;	
            }

            #loadingMessage
            {
                position:absolute;
                top:100px;
                left:100px;
                z-index:100;
                font-size:50px;
            }
        </style>

        <script>
            let canvas = null
            let ctx = null
            let mouseX = 0
            let mouseY = 0

            window.onload = onAllAssetsLoaded
            document.write("<div id='loadingMessage'>Loading...</div>")
            function onAllAssetsLoaded()
            {
                // hide the webpage loading message
                document.getElementById('loadingMessage').style.visibility = "hidden"

                canvas = document.getElementById("keyboardCanvas")
                ctx = canvas.getContext("2d")
                canvas.width = canvas.clientWidth
                canvas.height = canvas.clientHeight

                canvas.addEventListener('click', clickHandler)
            }


            function clickHandler(e)
            {
                if (e.which === 1)
                {
                    let canvasBoundingRectangle = canvas.getBoundingClientRect()
                    mouseX = e.clientX - canvasBoundingRectangle.left
                    mouseY = e.clientY - canvasBoundingRectangle.top

                    alert(`x: ${mouseX}    y: ${mouseY}`)
                }

            }


        </script>
    </head>

    <body>
        <canvas id = "keyboardCanvas" tabindex="1">
            Your browser does not support the HTML5 'Canvas' tag.
        </canvas>
        <p>Click the mouse on the canvas to get the current x,y position within the canvas.<br>
            The coordinates will still work after the browser has been resize. Try resizing the browser to test this.<br>
            Note that this canvas is 500 by 500 pixels.</p>
    </body>
</html>

 

In the example below, a rectangle is drawn at the location of a mouse click.

Example of a mouse click event (Run Example)

<!DOCTYPE html>
<html>
    <head>
        <title>Course notes example code</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <style>
            #keyboardCanvas
            {
                border:1px solid black;
                width:500px;
                height:500px;
                cursor:default	
            }

            #loadingMessage
            {
                position:absolute;
                top:100px;
                left:100px;
                z-index:100;
                font-size:50px;
            }
        </style>

        <script>
            let imageWidth = 100
            let imageHeight = 100

            let canvas = null
            let ctx = null
            let mouseX = 0
            let mouseY = 0

            window.onload = onAllAssetsLoaded
            document.write("<div id='loadingMessage'>Loading...</div>")
            function onAllAssetsLoaded()
            {
                // hide the webpage loading message
                document.getElementById('loadingMessage').style.visibility = "hidden"

                canvas = document.getElementById("keyboardCanvas")
                ctx = canvas.getContext("2d")
                canvas.width = canvas.clientWidth
                canvas.height = canvas.clientHeight

                renderCanvas()

                canvas.addEventListener('click', clickHandler)
            }


            function renderCanvas()
            {
                ctx.clearRect(0, 0, canvas.width, canvas.height)
                ctx.fillRect(mouseX, mouseY, imageWidth, imageHeight)
            }


            function clickHandler(e)
            {
                if (e.which === 1)
                {
                    let canvasBoundingRectangle = canvas.getBoundingClientRect()
                    mouseX = e.clientX - canvasBoundingRectangle.left - (imageWidth / 2)
                    mouseY = e.clientY - canvasBoundingRectangle.top - (imageHeight / 2)

                    renderCanvas()
                }
            }
        </script>
    </head>

    <body>
        <canvas id = "keyboardCanvas" tabindex="1">
            Your browser does not support the HTML5 'Canvas' tag.
        </canvas>
        <p>Click the mouse on the canvas to move the rectangle.</p>
    </body>
</html>

Write code to draw an image in a new location whenever the user clicks on the canvas, as shown in this link.

Dragging an Image

Before we can drag an image, we need to be able to detect

  1. if the mouse is inside the image
  2. the mouse offset position relative to the top-left corner of the image

The code below will detect if the location (x, y) is positioned inside an image.

function mouseIsInsideImage(imageTopLeftX, imageTopLeftY, imageWidth, imageHeight, x, y)
{
if ((x > imageTopLeftX) && (y > imageTopLeftY))
{
if (x > imageTopLeftX)
{
if ((x - imageTopLeftX) > imageWidth)
{
return false // to the right of the image
}
}
if (y > imageTopLeftY)
{
if ((y - imageTopLeftY) > imageHeight)
{
return false // below the image
}
}
}
else // above or to the left of the image
{
return false
}
return true // inside image
}

 

The offset needs to be calculated when the mouse is pressed down on an image. The code below will calculate the offsetX and offsetY of the mouse within an image.

function mousedownHandler(e)
{
if (e.which === 1) // left mouse button
{
if (mouseIsInsideImage(x, y, imageWidth, imageHeight, e.x, e.y))
{
offsetX = e.x - x offsetY = e.y - y }
}
}

Whenever an image changes position, the offset needs to be taken into account when calculating the new image top-left x and y positions. This is done by subtracting the offset values that were calculated in the mousedownHandler(e) code above.

x = e.x - offsetX
y = e.y - offsetY

Example that detects if the mouse is inside an image and takes the x and y offsets into account when dragging an image (Run Example)

<!DOCTYPE html>
<html>
    <head>
        <title>Course notes example code</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <style>
            #canvas
            {	
                width:500px;
                height:500px;
            }

            #canvas
            {
                border:1px solid black;
                cursor:default;
            }

            #text
            {
                clear:both;
            }

            #loadingMessage
            {
                position:absolute;
                top:100px;
                left:100px;
                z-index:100;
                font-size:50px;
            }
        </style>

        <script>
            let canvas = null
            let ctx = null
            let imageWidth = 100
            let imageHeight = 100
            let imageX = 250
            let imageY = 250

            let img = new Image()
            img.src = "images/dkit01.png"

            let offsetX = 0
            let offsetY = 0

            window.onload = onAllAssetsLoaded
            document.write("<div id='loadingMessage'>Loading...</div>")
            function onAllAssetsLoaded()
            {
                // hide the webpage loading message
                document.getElementById('loadingMessage').style.visibility = "hidden"

                canvas = document.getElementById("canvas")
                ctx = canvas.getContext("2d")
                canvas.width = canvas.clientWidth
                canvas.height = canvas.clientHeight

                renderCanvas()

                canvas.addEventListener('mousedown', mousedownHandler)
                canvas.addEventListener('mousemove', moveHandler)
            }


            function renderCanvas()
            {
                ctx.clearRect(0, 0, canvas.width, canvas.height)
                ctx.drawImage(img, imageX, imageY, imageWidth, imageHeight)
            }


            function mousedownHandler(e)
            {
                if (e.which === 1)  // left mouse button
                {
                    let canvasBoundingRectangle = canvas.getBoundingClientRect()
                    mouseX = e.clientX - canvasBoundingRectangle.left
                    mouseY = e.clientY - canvasBoundingRectangle.top
                    if (mouseIsInsideImage(imageX, imageY, imageWidth, imageHeight, mouseX, mouseY))
                    {
                        offsetX = mouseX - imageX
                        offsetY = mouseY - imageY
                    }
                }
            }


            function moveHandler(e)
            {
                if (e.which === 1)  // left mouse button
                {
                    let canvasBoundingRectangle = canvas.getBoundingClientRect()
                    mouseX = e.clientX - canvasBoundingRectangle.left
                    mouseY = e.clientY - canvasBoundingRectangle.top
                    if (mouseIsInsideImage(imageX, imageY, imageWidth, imageHeight, mouseX, mouseY))
                    {
                        ctx.clearRect(0, 0, canvas.width, canvas.height)
                        imageX = mouseX - offsetX
                        imageY = mouseY - offsetY
                        renderCanvas()
                    }
                }
            }


            function mouseIsInsideImage(imageTopLeftX, imageTopLeftY, imageWidth, imageHeight, x, y)
            {
                if ((x > imageTopLeftX) && (y > imageTopLeftY))
                {
                    if (x > imageTopLeftX)
                    {
                        if ((x - imageTopLeftX) > imageWidth)
                        {
                            return false // to the right of the image
                        }
                    }

                    if (y > imageTopLeftY)
                    {
                        if ((y - imageTopLeftY) > imageHeight)
                        {
                            return false // below the image
                        }
                    }
                }
                else // above or to the left of the image
                {
                    return false
                }
                return true // inside image
            }
        </script>
    </head>

    <body>
        <canvas id = "canvas">
            Your browser does not support the HTML5 'Canvas' tag.
        </canvas>
        <div id = 'text'>
            <p>Press the left mouse button on the image and drag the mouse to move the image.</p>
        </div>
    </body>
</html>

Write code to make a drawing tool that is similar to the one shown at this link.

Adjust the tool above, so that the user can select a colour from a colour picker, as shown here. Remember to initialise the colour picker to match the initial red colour of the brush.

Adjust the tool above, so that the user can select the brush width using a slider, as shown here. Remember to adjust the slider whenever the user presses the "thin" or "thick" button.

We can use an offscreen canvas to store the scribbles. This will allow us to seperate the background image from the scribbles. Use an offscreen canvas to allow the user to change the background image without losing the scribbles, as shown here.

Amend the scribble canvas so that it includes a save button, as shown here. Hint, there is a hyperlink <a> tag 'download' property that you can use. You should make the <a> tag look like a button using CSS.

What additional features can you add to improve this drawing tool?

We can associate a function with the mouse wheel using the code below

canvas.addEventListener('wheel', mouseWheelHandler)

The function is provided with a system variable, e. This variable contains the wheel movement in the variable e.wheelDelta. This value increments/decrements in units of 120. Therefore, we need to devide it by 120 to get a unit increment/decrement value.

function mouseWheelHandler(e) 
{
unitChange = e.wheelDelta / 120 // unitChange will be equal to either +1 or -1
// code to use the unitChange value is placed below }

Write code to scale an image using the mouse wheel, as shown in here. Note, that you should make sure that scaling only occurs if the mouse is over the image.

Write code to move and scale an image, as shown in here .

Expand the code above, so that the user can select between multiple images. You should highlight the currently selected image and apply the dragging and scaling to the selected image, as shown here.  

Expand the code above, so that the user can also rotate any of the multiple images, as shown here. Hint: You need to use a double buffer.

Extend the code above, so that the user can also adjust the brightness and toggle greyscale on any of the multiple images, as shown here.

Keyboard Events

There are three keyboard events:

onkeydown
The event occurs when as soon as any key has been pressed.
onkeypress
The event occurs when any key is being pressed.
onkeyup
The event occurs when a key is being released.
 

As with the mouse event, we need to insert two pieces of code in order to use a keyboard event: a function containing the action and an event listener. Unlike the mouse event, the canvas cannot detect keyboard events. Therefore, we tie the keyboard event to the html document.

document.addEventListener('keydown', keydownHandler)

 

When using keyboard events, we need to be able to detect which key has been pressed. The code below will test if the left arrow has been pressed.

function keydownHandler(e) 
{
   if (e.keyCode == 37)  // left arrow key
   {
      x -= stepSize
   }
}

In the example above, the variable, 'e' is provided by the system. It provides access to the keyCode of the key that has been pressed. Use this link to get the keyCode for any key. The complete set of keyCodes can be found at this link.

 

The example below allows the user to move a rectangle around the canvas using the four arrow keys.

Example of a keyboard event (Run example)

<!DOCTYPE html>
<html>
    <head>
        <title>Course notes example code</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <style>
            #keyboardCanvas
            {
                border:1px solid black;
                width:500px;
                height:500px;
            }

            #loadingMessage
            {
                position:absolute;
                top:100px;
                left:100px;
                z-index:100;
                font-size:50px;
            }
        </style>

        <script>
            let imageWidth = 100
            let imageHeight = 100

            let canvas = null
            let ctx = null
            let blackSquareX = 0
            let blackSquareY = 0

            window.onload = onAllAssetsLoaded
            document.write("<div id='loadingMessage'>Loading...</div>")
            function onAllAssetsLoaded()
            {
                // hide the webpage loading message
                document.getElementById('loadingMessage').style.visibility = "hidden"

                canvas = document.getElementById("keyboardCanvas")
                ctx = canvas.getContext("2d")
                canvas.width = canvas.clientWidth
                canvas.height = canvas.clientHeight

                renderCanvas()

                document.addEventListener('keydown', keydownHandler)
            }


            function renderCanvas()
            {
                ctx.clearRect(0, 0, canvas.width, canvas.height)
                ctx.fillRect(blackSquareX, blackSquareY, imageWidth, imageHeight)
            }


            function keydownHandler(e)
            {
                let stepSize = 10

                if (e.keyCode === 37)  // left
                {
                    blackSquareX -= stepSize
                }
                else if (e.keyCode === 38) // up
                {
                    blackSquareY -= stepSize
                }
                else if (e.keyCode === 39) // right
                {
                    blackSquareX += stepSize
                }
                else if (e.keyCode === 40) // down
                {
                    blackSquareY += stepSize
                }

                renderCanvas()
            }
        </script>
    </head>
    <body>
        <canvas id = "keyboardCanvas" tabindex="1">
            Your browser does not support the HTML5 'Canvas' tag.
        </canvas>
        <p>Use the four arrow keys to move the square.</p>
    </body>
</html>

Amend the above code to move an image around the screen.

Amend the above code to stop the image moving off the canvas.

Write code to animate a sprite character walking, as shown here. The sprite image is below:

 
<div align="center"><a href="../versionC/index.html" title="DKIT Lecture notes homepage for Derek O&#39; Reilly, Dundalk Institute of Technology (DKIT), Dundalk, County Louth, Ireland. Copyright Derek O&#39; Reilly, DKIT." target="_parent" style='font-size:0;color:white;background-color:white'>&nbsp;</a></div>