HTML5 Canvas Game

Introduction

The <canvas> element is one of the most popular elements of HTML5. It is widely supported by popular browsers like Firefox, Chrome, Safari and Opera (Internet Explorer supports it in their IE9 beta version).

This article describe the use of the HTML5′s <canvas> element through a fun example of bouncing multiple balls.

What is Canvas?

The <canvas> element is used to draw graphics on a web page by scripting.

The <canvas> element is only a container for graphics. You must use a script to actually draw the graphics. Canvas has several methods for drawing paths, boxes, circles, characters, and adding images.

The <canvas> element has only two attributes, width and height. These are both optional and can also be set using DOM properties. When no width and height attributes are specified, the canvas will initially be 300 pixels wide and 150 pixels high. The element can be sized arbitrarily by CSS, but during rendering the image is scaled to fit its layout size.

The <canvas> element can be styled just like any normal image (margin, border, background, etc).

Canvas Element

Let's add our <canvas> element inside the <body> tag. Though we only have one <canvas> element in our HTML for this example, I have still assigned an ID to it (myCanvas) just to make it easier to select it later on via JavaScript. I also defined the element's dimensions (width and height), however, you could just as well do that via CSS by targeting the #myCanvas ID.

JavaScript

Let's begin with our actual work of creating our shapes in JavaScript.

Drawing Balls

We will draw the balls using the arc( ) and fill( ) methods. Basically, we define the context, initiate the drawing, then we use color and style methods to fill in the color and dictate the shape (using a Math object for the ball).

The script code has a set of variables and  functions: initBalls( ), getMousePos( ), updateBalls( ), Ball( ) and animate( ). 

getMousePos( ) method

It gets the position of the Canvas. It then gets the relative mouse position.

function getMousePos(canvas, evt)

{

            // get canvas position

            var obj = canvas;

            var top = 0;

            var left = 0;

            while (obj.tagName != 'BODY')

            {

                top += obj.offsetTop;

                left += obj.offsetLeft;

                obj = obj.offsetParent;

            }

           // return relative mouse position

            var mouseX = evt.clientX - left + window.pageXOffset;

            var mouseY = evt.clientY - top + window.pageYOffset;

            return {

                x: mouseX,

                y: mouseY

            };

}

updateBalls( ) method

In this method, we will set the following features.

Set the position of the balls based on velocity

ball.y += ball.vy;
ball.x += ball.vx;

Setting the Restore Force

if (ball.x > ball.origX)

{

    ball.vx -= restoreForce;

}

else

{

     ball.vx += restoreForce;

}

if (ball.y > ball.origY)

{

    ball.vy -= restoreForce;

}

else

{

    ball.vy += restoreForce;

}

Setting the mouse force

   var mouseX = mousePos.x;

   var mouseY = mousePos.y;

 

   var distX = ball.x - mouseX;

   var distY = ball.y - mouseY;

 

   var radius = Math.sqrt(Math.pow(distX, 2) + Math.pow(distY, 2));

 

   var totalDist = Math.abs(distX) + Math.abs(distY);

 

   var forceX = (Math.abs(distX) / totalDist) * (1 / radius) * mouseForceMultiplier;

   var forceY = (Math.abs(distY) / totalDist) * (1 / radius) * mouseForceMultiplier;

Setting the mouse on the left of the Ball

if (distX > 0)

{

    ball.vx += forceX;

}

else

{

    ball.vx -= forceX;

}

Setting the mouse on the Right of the ball

 

if (distY > 0)

{

    ball.vy += forceY;

}

else

{

     ball.vy -= forceY;

}

 

Setting the floor friction

 

if (ball.vx > 0)

{

    ball.vx -= floorFriction;

}

else if (ball.vx < 0)

{

     ball.vx += floorFriction;

}

if (ball.vy > 0)

{

      ball.vy -= floorFriction;

}

else if (ball.vy < 0)

{

       ball.vy += floorFriction;

}

Limiting the area of the boundary

Now it's time to bounce the ball off the corners of the <canvas> element.

Setting up the limit for the floor condition

if (ball.y > (canvas.height - ball.radius))

{

     ball.y = canvas.height - ball.radius - 2;

     ball.vy *= -1;

     ball.vy *= (1 - collisionDamper);

}

 

Setting up the limit for the ceiling condition

 

if (ball.y < (ball.radius))

{

      ball.y = ball.radius + 2;

      ball.vy *= -1;

      ball.vy *= (1 - collisionDamper);

}

 

Setting up the limit for the right wall condition

 

if (ball.x > (canvas.width - ball.radius))

{

      ball.x = canvas.width - ball.radius - 2;

      ball.vx *= -1;

      ball.vx *= (1 - collisionDamper);

}

 

Setting up the limit the left wall condition

 

if (ball.x < (ball.radius))

{

       ball.x = ball.radius + 2;

       ball.vx *= -1;

       ball.vx *= (1 - collisionDamper);

}

Creating the ball constructor

function Ball(x, y, vx, vy, color)

{

    this.x = x;

    this.y = y;

    this.vx = vx;

    this.vy = vy;

    this.color = color;

    this.origX = x;

    this.origY = y;

    this.radius = 10;

}

 

Setting the mouse position really far away so the mouse forces are nearly obsolete

 

var mousePos = {x: 9999, y: 9999};

 

Move the Balls

 

Now that we have the balls, let's try to move them. We'll replace the hardcoded values of the coordinates in the .arc method with variables x and y, that we will then increment by an amount of dx and dy.

  • //move balls
    ball.y += ball.vy;
    ball.x += ball.vx;
     

  • //draw it
    context.arc(ball.x, ball.y, ball.radius, 0, 2 * Math.PI, false);
    context.fillStyle = ball.color;
    context.fill();

Example

 

<!DOCTYPE html>

 

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">

<head>

    <meta charset="utf-8" />

    <title>Canvas Game</title>

    <style>

        body {

            margin: 0px;

            padding: 0px;

        }

    </style>

</head>

<body>

    <canvas id="myCanvas" width="578" height="200" style="border: 2px solid black;"></canvas>

    <script>

        window.requestAnimFrame = (function (callback) {

            return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||

            function (callback) {

                window.setTimeout(callback, 1000 / 60);

            };

        })();

 

        function initBalls() {

            balls = [];

 

            var blue = '#3A5BCD';

            var red = '#EF2B36';

            var yellow = '#FFC636';

            var green = '#02A817';

 

            // G

            balls.push(new Ball(173, 63, 0, 0, blue));

            balls.push(new Ball(158, 53, 0, 0, blue));

            balls.push(new Ball(143, 52, 0, 0, blue));

            balls.push(new Ball(130, 53, 0, 0, blue));

            balls.push(new Ball(117, 58, 0, 0, blue));

            balls.push(new Ball(110, 70, 0, 0, blue));

            balls.push(new Ball(102, 82, 0, 0, blue));

            balls.push(new Ball(104, 96, 0, 0, blue));

            balls.push(new Ball(105, 107, 0, 0, blue));

            balls.push(new Ball(110, 120, 0, 0, blue));

            balls.push(new Ball(124, 130, 0, 0, blue));

            balls.push(new Ball(139, 136, 0, 0, blue));

            balls.push(new Ball(152, 136, 0, 0, blue));

            balls.push(new Ball(166, 136, 0, 0, blue));

            balls.push(new Ball(174, 127, 0, 0, blue));

            balls.push(new Ball(179, 110, 0, 0, blue));

            balls.push(new Ball(166, 109, 0, 0, blue));

            balls.push(new Ball(156, 110, 0, 0, blue));

 

            // O

            balls.push(new Ball(210, 81, 0, 0, red));

            balls.push(new Ball(197, 91, 0, 0, red));

            balls.push(new Ball(196, 103, 0, 0, red));

            balls.push(new Ball(200, 116, 0, 0, red));

            balls.push(new Ball(209, 127, 0, 0, red));

            balls.push(new Ball(223, 130, 0, 0, red));

            balls.push(new Ball(237, 127, 0, 0, red));

            balls.push(new Ball(244, 114, 0, 0, red));

            balls.push(new Ball(242, 98, 0, 0, red));

            balls.push(new Ball(237, 86, 0, 0, red));

            balls.push(new Ball(225, 81, 0, 0, red));

 

            // O

            var oOffset = 67;

            balls.push(new Ball(oOffset + 210, 81, 0, 0, yellow));

            balls.push(new Ball(oOffset + 197, 91, 0, 0, yellow));

            balls.push(new Ball(oOffset + 196, 103, 0, 0, yellow));

            balls.push(new Ball(oOffset + 200, 116, 0, 0, yellow));

            balls.push(new Ball(oOffset + 209, 127, 0, 0, yellow));

            balls.push(new Ball(oOffset + 223, 130, 0, 0, yellow));

            balls.push(new Ball(oOffset + 237, 127, 0, 0, yellow));

            balls.push(new Ball(oOffset + 244, 114, 0, 0, yellow));

            balls.push(new Ball(oOffset + 242, 98, 0, 0, yellow));

            balls.push(new Ball(oOffset + 237, 86, 0, 0, yellow));

            balls.push(new Ball(oOffset + 225, 81, 0, 0, yellow));

 

            // G

            balls.push(new Ball(370, 80, 0, 0, blue));

            balls.push(new Ball(358, 79, 0, 0, blue));

            balls.push(new Ball(346, 79, 0, 0, blue));

            balls.push(new Ball(335, 84, 0, 0, blue));

            balls.push(new Ball(330, 98, 0, 0, blue));

            balls.push(new Ball(334, 111, 0, 0, blue));

            balls.push(new Ball(348, 116, 0, 0, blue));

            balls.push(new Ball(362, 109, 0, 0, blue));

            balls.push(new Ball(362, 94, 0, 0, blue));

            balls.push(new Ball(355, 128, 0, 0, blue));

            balls.push(new Ball(340, 135, 0, 0, blue));

            balls.push(new Ball(327, 142, 0, 0, blue));

            balls.push(new Ball(325, 155, 0, 0, blue));

            balls.push(new Ball(339, 165, 0, 0, blue));

            balls.push(new Ball(352, 166, 0, 0, blue));

            balls.push(new Ball(367, 161, 0, 0, blue));

            balls.push(new Ball(371, 149, 0, 0, blue));

            balls.push(new Ball(366, 137, 0, 0, blue));

 

            // L

            balls.push(new Ball(394, 49, 0, 0, green));

            balls.push(new Ball(381, 50, 0, 0, green));

            balls.push(new Ball(391, 61, 0, 0, green));

            balls.push(new Ball(390, 73, 0, 0, green));

            balls.push(new Ball(392, 89, 0, 0, green));

            balls.push(new Ball(390, 105, 0, 0, green));

            balls.push(new Ball(390, 118, 0, 0, green));

            balls.push(new Ball(388, 128, 0, 0, green));

            balls.push(new Ball(400, 128, 0, 0, green));

 

            // E

            balls.push(new Ball(426, 101, 0, 0, red));

            balls.push(new Ball(436, 98, 0, 0, red));

            balls.push(new Ball(451, 95, 0, 0, red));

            balls.push(new Ball(449, 83, 0, 0, red));

            balls.push(new Ball(443, 78, 0, 0, red));

            balls.push(new Ball(430, 77, 0, 0, red));

            balls.push(new Ball(418, 82, 0, 0, red));

            balls.push(new Ball(414, 93, 0, 0, red));

            balls.push(new Ball(412, 108, 0, 0, red));

            balls.push(new Ball(420, 120, 0, 0, red));

            balls.push(new Ball(430, 127, 0, 0, red));

            balls.push(new Ball(442, 130, 0, 0, red));

            balls.push(new Ball(450, 125, 0, 0, red));

 

            return balls;

        }

        function getMousePos(canvas, evt) {

 

            var obj = canvas;

            var top = 0;

            var left = 0;

            while (obj.tagName != 'BODY') {

                top += obj.offsetTop;

                left += obj.offsetLeft;

                obj = obj.offsetParent;

            }

 

            var mouseX = evt.clientX - left + window.pageXOffset;

            var mouseY = evt.clientY - top + window.pageYOffset;

            return {

                x: mouseX,

                y: mouseY

            };

        }

        function updateBalls(canvas, balls, timeDiff, mousePos) {

            var context = canvas.getContext('2d');

            var collisionDamper = 0.3;

            var floorFriction = 0.0005 * timeDiff;

            var mouseForceMultiplier = 1 * timeDiff;

            var restoreForce = 0.002 * timeDiff;

 

            for (var n = 0; n < balls.length; n++) {

                var ball = balls[n];

 

                ball.y += ball.vy;

                ball.x += ball.vx;

 

                if (ball.x > ball.origX) {

                    ball.vx -= restoreForce;

                }

                else {

                    ball.vx += restoreForce;

                }

                if (ball.y > ball.origY) {

                    ball.vy -= restoreForce;

                }

                else {

                    ball.vy += restoreForce;

                }

 

                var mouseX = mousePos.x;

                var mouseY = mousePos.y;

 

                var distX = ball.x - mouseX;

                var distY = ball.y - mouseY;

 

                var radius = Math.sqrt(Math.pow(distX, 2) + Math.pow(distY, 2));

 

                var totalDist = Math.abs(distX) + Math.abs(distY);

 

                var forceX = (Math.abs(distX) / totalDist) * (1 / radius) * mouseForceMultiplier;

                var forceY = (Math.abs(distY) / totalDist) * (1 / radius) * mouseForceMultiplier;

 

                if (distX > 0) {

                    ball.vx += forceX;

                }

                else {

                    ball.vx -= forceX;

                }

                if (distY > 0) {

                    ball.vy += forceY;

                }

                else {

                    ball.vy -= forceY;

                }

 

                if (ball.vx > 0) {

                    ball.vx -= floorFriction;

                }

                else if (ball.vx < 0) {

                    ball.vx += floorFriction;

                }

                if (ball.vy > 0) {

                    ball.vy -= floorFriction;

                }

                else if (ball.vy < 0) {

                    ball.vy += floorFriction;

                }

 

                if (ball.y > (canvas.height - ball.radius)) {

                    ball.y = canvas.height - ball.radius - 2;

                    ball.vy *= -1;

                    ball.vy *= (1 - collisionDamper);

                }

 

                if (ball.y < (ball.radius)) {

                    ball.y = ball.radius + 2;

                    ball.vy *= -1;

                    ball.vy *= (1 - collisionDamper);

                }

 

                if (ball.x > (canvas.width - ball.radius)) {

                    ball.x = canvas.width - ball.radius - 2;

                    ball.vx *= -1;

                    ball.vx *= (1 - collisionDamper);

                }

 

                if (ball.x < (ball.radius)) {

                    ball.x = ball.radius + 2;

                    ball.vx *= -1;

                    ball.vx *= (1 - collisionDamper);

                }

            }

        }

        function Ball(x, y, vx, vy, color) {

            this.x = x;

            this.y = y;

            this.vx = vx;

            this.vy = vy;

            this.color = color;

            this.origX = x;

            this.origY = y;

            this.radius = 10;

        }

        function animate(canvas, balls, lastTime, mousePos) {

            var context = canvas.getContext('2d');

 

            var date = new Date();

            var time = date.getTime();

            var timeDiff = time - lastTime;

            updateBalls(canvas, balls, timeDiff, mousePos);

            lastTime = time;

 

            context.clearRect(0, 0, canvas.width, canvas.height);

 

            for (var n = 0; n < balls.length; n++) {

                var ball = balls[n];

                context.beginPath();

                context.arc(ball.x, ball.y, ball.radius, 0, 2 * Math.PI, false);

                context.fillStyle = ball.color;

                context.fill();

            }

            requestAnimFrame(function () {

                animate(canvas, balls, lastTime, mousePos);

            });

        }

        var canvas = document.getElementById('myCanvas');

        var balls = initBalls();

        var date = new Date();

        var time = date.getTime();

 

        var mousePos = {

            x: 9999,

            y: 9999

        };

        canvas.addEventListener('mousemove', function (evt) {

            var pos = getMousePos(canvas, evt);

            mousePos.x = pos.x;

            mousePos.y = pos.y;

        });

        canvas.addEventListener('mouseout', function (evt) {

            mousePos.x = 9999;

            mousePos.y = 9999;

        });

        animate(canvas, balls, time, mousePos);

    </script>

</body>

</html>

 

Output


Initially at the beginning we get:


game1.jpg


On mouseHover we get:


game2.jpg


Finally at last we get:


game3.jpg


Up Next
    Ebook Download
    View all
    Learn
    View all