I have seen this background animation many times all over CodePen and internet in general. It is pleasent to spectate and is not distracting when the user is busy dealing with other features of the app/website.
#But
Just why is this so complicated? I have even seen this animation implemented with 150 lines! From the side, animation seems to be very easy to code and there isn’t much to it other than particles changing their direction on touch with the wall. There is no acceleration, there is no need for vectors, there is no user interaction at all.
#Let’s just fix it.
#i assume you know the basics of HTML5 2D canvas API
So, where do we begin? Lets have a general idea of what is happening:
- There are particles of different
- speed
- opacity
- size
- Particles are going in the opposite direction after they hit the wall.
You might say: “well, that is pretty simple, I guess“. I know right?
#Step #0. Onto the initial setup
If you have read my articles/tutorials before, then you know I use basically the same structure every time. This time is no different.
#HTML
<canvas id=c></canvas>
The id=c
will turn into a global variable. We won’t need to do getElementsById('c')
because, apparently, it is already a global variable. Don’t ask me why.
#CSS
canvas {
position: absolute;
top: 0px;
left: 0px;
}
We are just removing the ugly scroll-bars, by making the body
height to be automatically 0
, because the contents are position: absolute;
.
#JS
(()=>{
let $ = c.getContext("2d"),
w = c.width = window.innerWidth,
h = c.height = window.innerHeight,
//predefines...
})()
Again, things are obvious. We are defining the context variable, and assigning the variables w
and h
and the canvas’s width
and height
at the same time to the window’s width
and height
.
let pi2 = Math.PI*2,
random = t=>Math.random()*t,
binRandom = t=>Math.random()<t;
PI2
and these two functions will be used many times later, so I created a variable for them.
random(n) returns
Math.random()
multiplied byn
and
binRandom(n) (from binary random) will return true if
Math.random()
is smaller thann
Let’s just display something on our screen real quick so that we know we are doing something:
function draw(){
$.fillStyle="#222";
$.fillRect(0,0,w,h);
requestAnimationFrame(draw);
}
draw(); //do not forget to call the function
Just a background for our bouncing particles that we are about to make.
#Step #1. Let’s get to particles.
Let’s review what we see as particles
Particle is an object that has it’s own:
- Size
- Position
- Speed
- Opacity
now that we know that, let’s just fill one array with many particles like that:
let arr = new Array(500 /* amount */).fill().map(p=>{
return {
p: {x: random(w), y: random(h) }, //position
v: { //velocity
x: binRandom(0.5)? random(1) : random(-1),
y: binRandom(0.5)? random(1) : random(-1)
},
s: random(1)+2, //size
o: random(1)+.3 //opacity
}
})
There is much stuff being defined here so let’s go step-by-step.
#Array processing
- We create an array. The array is empty (!) even though it has the length of 500. The
map()
function maps through each object in the array and returns a new one if return is specified. We can’t run the functionmap()
yet because, as I mentioned, thearr
is empty, hence there is nothing tomap
through. - We
fill()
the array withundefined
elements. Even though each element in the array is still undefined, it is there and the array is not empty anymore. - We
map()
through eachundefined
object in the array andreturn
a new object that we are going to discuss right now.
#Particle values
- Position is random across the width and height of the screen.
- Velocity would look really stupid if the direction is only positive. That is why, we are using
binRandom(.5)
to have a random speed in random directions. - Size This whole
+2
addition’s purpose is obvious – to set the minimal value to two, and then it would be up to 3 withMath.random()
- Opacity Same trick. Minimal opacity of
.3
. Notice that opacity can also be a max of1.3
. That means that there will be more particles that are not transparent at all. (if that is a good or a bad, depends on your taste).
#Step #2. Let’s render the particles.
If you know basic Canvas API, you can even skip this part – it’s too easy.
function draw(){
//stuff
arr.forEach(p=>{
$.fillStyle="rgba(255,255,255,"+p.o+")";
$.beginPath();
$.arc(p.p.x, p.p.y, p.s, 0, pi2);
$.closePath();
$.fill();
})
}
Well, there isn’t much to explain. We draw the circle with opacity of p.o
, size of p.s
, at p.p.x
and p.p.y
.
What we will see on our screen is a bunch of circles of different opacity and size. But they aren’t moving and we are about to fix that.
#Step #3. Lets move the particles.
arr.forEach(p=>{
p.p.x += p.v.x;
p.p.y += p.v.y;
if (p.p.x > w || p.p.x < 0) p.v.x *= -1;
if (p.p.y > h || p.p.y < 0) p.v.y *= -1;
//rendering stuff
}
Those conditional functions are checking if the position on each axis is over the borders. If it does then velocity of the particle on that axis will be reflected.
I don’t know why, but positions are locked as soon as they touch the border if i put the position update after the conditional functions. I moved it before the conditional functions and it all worked fine.
At this point we should have about 30 lines of code, and you can see our amazing animation working.
#Step #4. Resizing.
I love CodePen because there are many genious people. I was always doing resizing through an eventListener
. On my previous tutorial Mark (@Blindman67) suggested one-liner replacement to my eventListener
to make it shorter:
(h !== innerHeight || w!==innerWidth) && (w=c.width=innerWidth,h=c.height=innerHeight);
And that’s it! We now have a very simple animation of drifting bouncing particles.
Deixe um comentário