Recreating Phase Beam in Canvas - Part 1
Recreating Android's ICS Phase Beam Live Wallpaper Using Canvas
- Part 1 - Creating the canvas with animating circles.
- Part 2 - Adding lines and making the circles blurry.
Here's my first series of tutorials. In this series, you will learn how to recreate the Android's ICS Phase Beam Live Wallpaper using HTML5's canvas. This is the first part of this this series in which I will teach you how to create and animate the circles. In the next part, I will teach you about how to make the circles blur and create lines too.
Step 1 - Creating the canvas
So let's start by adding a canvas
element into the HTML. We'll also need to include the jQuery.
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script> <canvas id="canvas"></canvas>
Now we need to add some CSS to prevent margins, paddings and visible scrollbars.
body {padding: 0px; margin: 0; overflow: hidden;}
Now you have created the canvas
, you need to initialize it's context with JS.
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d");
Here, the getContext
method initializes the canvas
with a 2d
context. You don't need to understand this much deeply.
After creating the canvas, we need to set it's height
and width
equal to the window's height
and width
which can be simply done by:
var W = window.innerWidth, H = window.innerHeight; canvas.width = W; canvas.height = H;
Step 2 - Fill the canvas with a diagonal linear gradient
As now you have initialized the canvas, it's time to add a nice background to it. We'll star off with creating a linear gradient. The linear gradient can be created by using the following code.
var grad = ctx.createLinearGradient(0, 0, W, H); grad.addColorStop(0, 'rgb(19, 105, 168)'); grad.addColorStop(0.9, 'rgb(0, 0, 0)');
The createLinearGradient
function accepts four parameters which can be defined as (start position X, start position Y, end position X, end position Y), so we just passed the starting values as 0,0 and ending values as the width and height of the canvas so that the gradient covers the full window.
Now, the second and the third lines are used to add color stops to the gradient by using addColorStop
method which is self-explanatory. To fill the canvas with the gradient we just created, we would need to do the following:
ctx.globalCompositeOperation = "source-over"; ctx.fillStyle = grad; ctx.fillRect(0,0,W,H);
The first line is to set the globalCompositeOperation
to source-over
so that the BG of current frame doesn't mix with the BG of previous frame. There are many values for globalCompositeOperation
which you can learn about here.
The second line sets the fillStyle
to the gradient we just created. You can also pass colors here, try passing "blue" instead of grad and see the result. The third line is for filling the canvas using a rectangle. The attributes are same as explained when we created the gradient. So now, you have a beautiful canvas background. Now we need to add some circles but before that, let's organize our code a little.
Move everything under window.onload = function() { //code here }
. You'll get the following code
window.onload = function() { var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); //Set the dimensions of canvas equal to the window's dimensions var W = window.innerWidth, H = window.innerHeight; canvas.width = W; canvas.height = H; //Create the gradient var grad = ctx.createLinearGradient(0, 0, W, H); grad.addColorStop(0, 'rgb(19, 105, 168)'); grad.addColorStop(0.9, 'rgb(0, 0, 0)'); //Fill the canvas with the gradient ctx.globalCompositeOperation = "source-over"; ctx.fillStyle = grad; ctx.fillRect(0,0,W,H); }
Also, when we will animate the circles, it would require to repaint the background in each frame so that it doesn't get merged with the circles or the background of previous frame. So, move the gradient code and the fill code under a new function draw()
and then call it somewhere after the dimensions specifications.
window.onload = function() { var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); //Set the dimensions of canvas equal to the window's dimensions var W = window.innerWidth, H = window.innerHeight; canvas.width = W; canvas.height = H; draw(); //Function to draw the background function draw() { //Create the gradient var grad = ctx.createLinearGradient(0, 0, W, H); grad.addColorStop(0, 'rgb(19, 105, 168)'); grad.addColorStop(0.9, 'rgb(0, 0, 0)'); //Fill the canvas with the gradient ctx.globalCompositeOperation = "source-over"; ctx.fillStyle = grad; ctx.fillRect(0,0,W,H); } }
Step 3 - Adding circles and animating them
The code looks much better and now we will create the circles and animate them. Start off by creating an empty array called circles
and a function to create the circles with random properties. This function will be used to fill the circles
array with circles having different properties like position, speed and radius. The code will look like
//Create an array of circles var circles = []; for(var i = 0; i < 20; i++ ){ circles.push(new create_circle()); } //Function to create circles with different positions and velocities function create_circle() { //Random Position this.x = Math.random()*W; this.y = Math.random()*H; //Random Velocities but same direction. this.vx = Math.random()*1; this.vy = -this.vx; //Random Radius this.r = 10 + Math.random()*50; }
Add this code above the draw()
function we created in the previous step. Now we need to place the circles on the canvas. To do this, we will modify our draw()
function and the following code to it at the end. The function create_circle()
is used to create a circle with random values / properties. Try to experiment with these to get different results. You can change the direction by passing a random value to this.vy
variable.
//Fill the canvas with the circles for(var j = 0; j < circles.length; j++) { var c = circles[j]; //Draw the circle and it with the blur grad ctx.beginPath(); ctx.globalCompositeOperation = "lighter"; ctx.fillStyle = grad; ctx.arc(c.x, c.y, c.r, Math.PI*2, false); ctx.fill(); //Lets use the velocity now c.x += c.vx; c.y += c.vy; //To prevent the circles from moving out of the canvas if(c.x < -50) c.x = W+50; if(c.y < -50) c.y = H+50; if(c.x > W+50) c.x = -50; if(c.y > H+50) c.y = -50; }
Here, to draw a circle, we have used the canvas function .arc(x offset, y offset, radius, angle, counterclockwise?)
. You will notice that here, globalCompositeOperation
has the value lighter
, this is to make the circles transparent. Try using different values in here like darker
. The circle is created by first calling the beginPath()
function, then we set the globalCompositeOperation
, then we set the fill style to the gradient we created earlier, then we create the circle and finally we fill the circle with the gradient.
After this, we increment the position of the circles according to their velocites and then there are some conditions to prevent them from moving outside the canvas. If they move outside, they'll be moved at the start so by this way, we won't have to add more circles to the canvas.
Now the final step is to modify the calling of our draw()
function. We want to animate the frames so here, we will use the setInterval()
function. The final code will look like this
window.onload = function() { //Create canvas and initialize it's context var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); //Set the dimensions of canvas equal to the window's dimensions var W = window.innerWidth, H = window.outerHeight; canvas.width = W; canvas.height = H; //Create an array of circles var circles = []; for(var i = 0; i < 20; i++ ){ circles.push(new create_circle()); } //Function to create circles with different positions and velocities function create_circle() { //Random Position this.x = Math.random()*W; this.y = Math.random()*H; //Random Velocities this.vx = Math.random()*1; this.vy = -this.vx; //Random Radius this.r = 10 + Math.random()*50; } //Function to draw the background function draw() { //Create the gradient var grad = ctx.createLinearGradient(0, 0, W, H); grad.addColorStop(0, 'rgb(19, 105, 168)'); grad.addColorStop(0.9, 'rgb(0, 0, 0)'); //Fill the canvas with the gradient ctx.globalCompositeOperation = "source-over"; ctx.fillStyle = grad; ctx.fillRect(0,0,W,H); //Fill the canvas with the circles for(var j = 0; j < circles.length; j++) { var c = circles[j]; //Draw the circle and it with the blur grad ctx.beginPath(); ctx.globalCompositeOperation = "lighter"; ctx.fillStyle = grad; ctx.arc(c.x, c.y, c.r, Math.PI*2, false); ctx.fill(); //Lets use the velocity now c.x += c.vx; c.y += c.vy; //To prevent the circles from moving out of the canvas if(c.x < -50) c.x = W+50; if(c.y < -50) c.y = H+50; if(c.x > W+50) c.x = -50; if(c.y > H+50) c.y = -50; } } setInterval(draw, 25); }
Make sure you type all the code instead of copying and pasting. Also, as you write the code, try experimenting with different values to get some interesting results. You can add various Math
functions to make the animation more interesting. Now show me your creations or if you have any question, then use the comment form below :)
In the next part, I will show how to make the circles blurry and create other particles like in the original live wallpaper.