Prayogo Cendra


Tutorial: Simple Bouncing Ball animation on HTML Canvas - Demo

Let's Create a simple bouncing ball animation

  1. Prepare our canvas

    <!DOCTYPE html>
    <html>
    <head>
    	<title>Bouncing Ball</title>
    </head>
    <body onload="init()">
    	<canvas id="my-canvas"></canvas>
    </body>
    </html>

  2. Set some CSS

    body {
    	margin: 0;
    	background-color: black;
    }
    #my-canvas {
    	width: 100%;
    	height: 100%;
    }
    We want our ball bouncing all over the page so set canvas width and height to 100%

  3. JavaScript, where all the fun begin
    1. Prepare all needed variables

      //canvas properties
      var canvas = document.getElementById("my-canvas");
      var context = canvas.getContext("2d");
      
      //animation things
      var fps = 60;
      var updateTime = 1000 / fps;
      
      //easy variables for easy coding life
      var _PI = Math.PI;
      var _sin = Math.sin;
      var _cos = Math.cos;
      var _random = Math.random;
      var oneDegreeOnRadian = _PI / 180;
      
      // ball
      var ball;

    2. Next, we create our bouncing ball class

      function Ball(radius, speed) {
      	this.radius = (radius) ? (radius) : (20); // ball size, default = 20
      	this.speed = (speed) ? (speed) : (10); // ball speed, default = 10
      	this.x = (_random() * (canvas.width - (2 * this.radius))) + this.radius; // random horizontal position
      	this.y = (_random() * (canvas.height - (2 * this.radius))) + this.radius; // random vertical position
      
      	var angleRadian = _random() * 360 * oneDegreeOnRadian; // random move angle direction
      
      	this.xSpeed = _cos(angleRadian) * this.speed; // speed on x axis
      	this.ySpeed = _sin(angleRadian) * this.speed; // speed on y axis
      
      	this.xDirection = (_random < 0.5) ? (1) : (-1); // x direction
      	this.yDirection = (_random < 0.5) ? (1) : (-1); // y direction
      
      	this.move = function() {
      		var nextX = this.x + (this.xDirection * this.xSpeed); // predict next x position
      		var nextY = this.y + (this.yDirection * this.ySpeed); // predict next y position
      
      		if(((nextX - this.radius) < 0) || ((nextX + this.radius) > canvas.width - 1)) { // if collide with left/right screen
      			this.xDirection *= -1; // change x direction
      		}
      		if(((nextY - this.radius) < 0) || ((nextY + this.radius) > canvas.height - 1)) { // if collide with top/bottom screen
      			this.yDirection *= -1; // change y direction
      		}
      
      		this.x += this.xDirection * this.xSpeed; // update x
      		this.y += this.yDirection * this.ySpeed; // update y
      	}
      
      	this.draw = function() {
      		context.fillStyle = "tomato"; // ball color
      		context.beginPath();
      		context.arc(this.x, this.y, this.radius, 0, _PI * 2, false); // draw circle
      		context.closePath();
      		context.fill(); // draw ball on canvas
      	}
      }

      Our bouncing class has properties such as:

      • radius - how big is the ball
      • speed - how fast will it move
      • x, y - coordinate of the ball on canvas
      • xSpeed - speed of ball on x axis (translated from speed and random angle)
      • ySpeed - speed of ball on y axis (translated from speed and random angle)
      • xDirection - specify the direction of the ball on x axis (-1 for left and 1 for right)
      • xDirection - specify the direction of the ball on y axis (-1 for up and 1 for down)
      • move() - function that we call to move the ball
      • draw() - function to draw this ball on canvas

    3. Next, we create functions to animate our bouncing ball

      function clear() {
      	context.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
      }
      
      function update() {
      	ball.move(); // move ball
      }
      
      function draw() {
      	ball.draw(); // draw the ball
      }
      
      function animate() {
      	clear();
      	update();
      	draw();
      }

    4. Add function to resize the canvas

      // update canvas size when resizing window 
      function resizeCanvas() {
      	canvas.width = window.innerWidth;
      	canvas.height = window.innerHeight;
      }

    5. Initialize and run our animation

      //initialize canvas
      function init() {
      	window.addEventListener('resize', resizeCanvas, false);
      	resizeCanvas();
      	ball = new Ball(50, 10);
      	setInterval(function() {
      		animate();
      	}, updateTime);
      }

      window.addEventListener('resize', resizeCanvas, false); - this add an event listener that will call resizeCanvas() when you resize the browser

  4. Finally, save your file and run on your favorite browser

    This is the final code

    <!DOCTYPE html>
    <html>
    <head>
    	<title>Bouncing Ball</title>
    	<style type="text/css">
    		body {
    			margin: 0;
    			background-color: black;
    		}
    		#my-canvas {
    			width: 100%;
    			height: 100%;
    		}
    	</style>
    </head>
    <body onload="init()">
    	<canvas id="my-canvas"></canvas>
    <script type="text/javascript">
    	//canvas properties
    	var canvas = document.getElementById("my-canvas");
    	var context = canvas.getContext("2d");
    
    	//animation things
    	var fps = 60;
    	var updateTime = 1000 / fps;
    
    	//easy variables for easy coding life
    	var _PI = Math.PI;
    	var _sin = Math.sin;
    	var _cos = Math.cos;
    	var _random = Math.random;
    	var oneDegreeOnRadian = _PI / 180;
    
    	// ball
    	var ball;
    
    	//Our bouncing ball class
    	function Ball(radius, speed) {
    		this.radius = (radius) ? (radius) : (20); // ball size
    		this.speed = (speed) ? (speed) : (10); // ball speed
    		this.x = (_random() * (canvas.width - (2 * this.radius))) + this.radius; // random horizontal position
    		this.y = (_random() * (canvas.height - (2 * this.radius))) + this.radius; // random vertical position
    
    		var angleRadian = _random() * 360 * oneDegreeOnRadian; // random move direction
    
    		this.xSpeed = _cos(angleRadian) * this.speed; // translate speed on x axis
    		this.ySpeed = _sin(angleRadian) * this.speed; // translate speed on y axis
    
    		this.xDirection = (_random < 0.5) ? (1) : (-1); // random first x direction
    		this.yDirection = (_random < 0.5) ? (1) : (-1); // random first x direction
    
    		this.move = function() {
    			var nextX = this.x + (this.xDirection * this.xSpeed); // predict next x position
    			var nextY = this.y + (this.yDirection * this.ySpeed); // predict next y position
    
    			if(((nextX - this.radius) < 0) || ((nextX + this.radius) > canvas.width - 1)) { // if collide with left/right screen
    				this.xDirection *= -1; // change x direction
    			}
    			if(((nextY - this.radius) < 0) || ((nextY + this.radius) > canvas.height - 1)) { // if collide with top/bottom screen
    				this.yDirection *= -1; // change y direction
    			}
    
    			this.x += this.xDirection * this.xSpeed; // update x
    			this.y += this.yDirection * this.ySpeed; // update y
    		}
    
    		this.draw = function() {
    			context.fillStyle = "tomato"; // ball color
    			context.beginPath();
    			context.arc(this.x, this.y, this.radius, 0, _PI * 2, false); // draw circle
    			context.closePath();
    			context.fill(); // draw ball on canvas
    		}
    	}
    
    	function clear() {
    		context.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
    	}
    
    	function update() {
    		ball.move(); // move ball
    	}
    
    	function draw() {
    		ball.draw();
    	}
    
    	function animate() {
    		clear();
    		update();
    		draw();
    	}
    
    	// update canvas size when resizing window 
    	function resizeCanvas() {
    		canvas.width = window.innerWidth;
    		canvas.height = window.innerHeight;
    	}
    
    	//initialize canvas
    	function init() {
    		window.addEventListener('resize', resizeCanvas, false);
    		resizeCanvas();
    		ball = new Ball(50, 10);
    		//run animation
    		setInterval(function() {
    			animate();
    		}, updateTime);
    	}
    </script>
    </body>
    </html>