Transcripts
1. Project 1: Slice & Dice Effects on Images: I want to introduce you to a special motion
physics formula. And once you know how it works, you will be ready to make the most epic
interactive effects. The full understanding
of what the code is doing is very
important if you want to have a complete control
and you want to be able to experiment and come up with your own new and unique effects. That's why, as always, we are using no frameworks
and no libraries, Only plain vanilla Javascript and object oriented programming. Let's learn some beginner friendly slice and
dice animations on images and cover three very important and fundamental techniques
that will get you ready for the ultimate set of image manipulation effects. As always, this class
is beginner friendly, but a basic understanding
of HTML, CSS, and Javascript is needed to be able to follow and
actually understand.
2. HTML, CSS & JavaScript setup: I create three files in my
project folder, index HTML, style CSS and script
S. Inside index HTML, I create a standard
simple web page. I give it some title. I link my style sheet, I create HTML five
canvas element with an ID of canvas one. And I link my script file. I will give it
defer attribute to make sure all the
contents of the page are loaded before the
script runs intysSs, I give my canvas
element a border. I center it in the
middle of the web page, both vertically
and horizontally. Using the absolute
position technique. Like this in script GS, we do the standard HTML canvas, set up four lines of code
that are always the same. First, we point Java script towards the canvas
element using its ID. Then we take that
canvas reference and we call a special
get context method. And we instantiate a built
into the canvas API like this. Now we can use this
CTX variable to call all canvas drawing methods
to access its properties. For this project, I
set canvas width to 500 pixels and height
will be 700 pixels. I make sure I set
the size here in Java script and not in
style CSS to make sure both element size and
drawing surface size of my canvas element are
set to these values. To prevent any distortions, we will be slicing
images into a grid. Each cell in that grid
will be a separate object. Here I have a blueprint
for that cell object. We will also create a class. I call for example, effect the main brain
of the code base. We will manage the state
of the entire effect. From here, constructor
will expect canvas as an argument inside I convert
it into a class property. Yes, I can pull canvas variable from line one directly
into my class. But I prefer to do it this
way to keep my classes more self contained from
that canvas reference. We will extract with and
height because I want the dimensions of the effect
to match the size of canvas. I create an instance of the
class using the new keyword. It expects canvas
as an argument, I pass it canvas
variable from line one. If I consol effect, we can see it has
three properties, canvas with and height. Everything works well so far.
3. How to organise anything into a grid: We want to slice the available
canvas area into a grid. The width of each cell
will be the width of canvas divided by,
let's say, 35. We want 35 cells per row. The height of each cell will be canvas height divided by 55. We want 55 rows, 35 columns, 55 rows. Let's use the cell
class from line six to first create
just one cell object. All the code inside
the class constructor is executed line by line when we create an instance of that class using
the new keyword. If I do this, an instance of cell class will be
created automatically. At the point when I create an instance of the effect class, I will consol Ok
cell so that we can see its properties pop up in the console
as we create them. It's a good way to
check if everything is working as we build
the project up, each cell in the grid
will need access to properties on the
main effect class. One way to do it is to create a reference pointing to the effect to the
space in memory. The main effect object
sits cells will be organized in a grid to
cover the entire canvas area. Each cell will expect
x and Y coordinates so that it knows where exactly
in the grid it should sit. I passed this keyword referencing this
entire effect class. At first, I want to draw
this cell at coordinate 00. Top left corner of canvas, we can see the cell
object correctly receiving the properties
here in the console. I want all the cells to have
the same width and height, so all of them will
have access to cell width and cell height
from the main effect object. Now we have enough information
about each cell to be able to actually draw it on
cannabas custom draw method. We'll expect context
as an argument. Again, I could directly
pull CTX from line two, but I prefer to make my classes more independent on
their lexical scope. I will do it this way. Each cell will be just a black rectangle. For now, I can draw it
from here if I want, but it wouldn't be practical. Since the image can be made
out of thousands of cells. We will draw all of them from a custom render
method like this. Now I call this render method
from my effect variable. I pass it CTX from line two. We want to draw multiple cells and I want to organize
them in a grid. The first cell will
be here horizontally. The second cell will be here. We will be jumping by cell width horizontally until we
cover the entire row. Then we will jump by cell height vertically
to the next row. We will repeat this until we cover the entire canvas area. I will hold all
cells in an array. I will call it image grid. To achieve this effect,
you need to understand two fundamental creative coding and two, the
animation techniques. Both are extremely important and can be used for thousands of different things just for the effect we
are building today. The first technique
we need to understand is how to organize
anything into a grid. We will do it here inside a
custom create grid method. To create a grid, we will
use two nested four loops. Nested means there is a loop
inside of another loop. The outer loop will
handle rows in the grid. We will cover canvas from
top to bottom, row by row. We will start from top from
vertical coordinate zero. We will be running
this four loop until we reach the
height of canvas, which means until we
reach the bottom, instead of jumping by one, we will always jump by cell height to make sure each
row has the correct height. Every time we jump to a new row, we will use another
four loop to cycle over all horizontal
cells on that row, from left to right, from
zero to canvas width. Let's write this
all out and we will quickly recap how
the logic works. Organizing objects in a grid is extremely useful technique to understand for creative coding and also for making two D games. For example, every time we jump to a new
cell in the grid, we take image grid array and
we push new cell in there, Passing it the reference
to the effect object. This time we pass it X index from the inner loop and Y index from the outer loop
to place the cell in the grid as we are
creating it row by row, from top to bottom. To recap, the outer
loop handles rows, the inner loop handles columns, the cells that sit horizontally
next to each other. Inside each row, let's
say we are on row zero, Y is a zero, We are up top. Here we enter the
inner loop and we push one horizontal cell
next to each other, jumping by cell width until
we reach the width of canvas. We exit the inner loop. The outer loop will
increase y by cell height. We jump to a new row again, we enter the inner loop, we place all the cells on
that row from left to right. When we reach the width, we exit the inner loop, increase Y by one, we enter another row. And we repeat this process
until we have x and y coordinates for each cell in the grid that covers
the entire canvas. It's a very useful
technique if you are a beginner and this is the
first time you see this. Don't worry, it will make more sense if you
use this more often. As we said, all of the code in the constructor is automatically executed when we
create an instance of that class using
the new keyword. For that reason, I can
automatically trigger methods. From here, I will call create grid so that when we create
an instance of effect class, a grid of cell objects is
automatically created as well get an error because we
don't have this cell here. Instead of this line, I will
call for each method on image grid on the
array from line 26. For each cell object we just
pushed into that array, I will call its draw method. I want to draw all
of the cells we just created in console. I can see that my array
contains 1,980 cell objects. I will stroke them instead so we can better see the
grid we just created. Nice work. I can
change these values to increase or decrease
the size of my cells.
4. How to draw, crop & slice images with code: Now I take an image. Ideally the image should be
the same size as canavas. In my case, that's
500 times 700 pixels. I put the image inside my project folder and
I reference it here. I give it an idea
of project image. I don't want to draw
the actual IMG element. I give it display none. We want to draw that
image on canvas. Instead, I bring it into my project here on the
main effect class. This image property
inside render, I call built in
draw image method. And I pass it the
image I want to draw and X and Y coordinates. Where to draw it, I can move
the image around like this. I can also give it optional
width and height arguments. And the image will
be stretched or squeezed to fit into
that predefined area. I want to slice the
image into pieces. So I will instead use
the longest version of draw image method
with nine arguments. This version gives
us the most control over the image we are
drawing or animating. We pass it the image
we want to draw, source x, source Y, source width, and source height, the area we want
to crop out from the source image and
destination X, destination Y, destination width, and
destination height to define where to draw that
cropped out piece of image. On destination Cannavas,
let's say I want to crop out this area between
coordinate 00.200 400. And I want to stretch that cropedout piece
over the entire canvas. We are stretching it from
coordinate 00 to canvas. With canvas height,
we have full control now These four values
determine the cropping area. These four values
determine where to draw that cropped
out piece of image. On Destination Canavas, I want each cell to contain
a small piece of image. I will cut this line. Instead, I will put this
image property on the cell. Now we are drawing
the image many times, once for each cell in the grid. We can't afford to
do this because canvas is actually very
efficient at drawing images. It doesn't have to
calculate anything. It just takes the image
data and it draws it. It's a cheap operation
compared to drawing a shape. If I draw the same image at x and y coordinates of
each cell, we get this. If I give it with height, each cell contains
the entire image. We already know that we need four more arguments to crop
out a piece of that image. Source X, source Y, source width, and source height. We already organized
these cells in a grade. We already have the values
of the crop in coordinates. We can just do this simple. Now, it looks like we are just drawing a single image again. But actually at this point, we are drawing a piece of
the image from each cell. And the cells combined together
create the full image. How can I prove
this? For example, I will use autogenerated index. Here two arguments
we need brackets. Only draw the cell with
index of 300 or 500. Only draw the cells
with an index higher than 500 or lower than 500. Lower than 600, 801,000 201,300, I think we have
proven the point. This is no longer
a single image, it's made out of many pieces.
5. How to animate images with code: For the next step, I will
create an animation loop. We will be updating some
values and redrawing the cells over and over again to create an illusion
of movement. I put render inside I call built in request
animation frame. And I call animate to
kick the animation off. It looks static, but we
are already animating. I can prove this. I give each cell slide X and
slide Y properties. Very different things
will happen depending on where inside the draw
method we add these. I will add these to
source X and source Y coordinates that determine
the cropping area. But we could also add
them to destination X and destination Y for a completely
different set of effects. If each cell has the
same slight value, the entire image will move. But what will happen
if we randomize them? This randomizes them just
once on the first page load. What if I create an
update method and I randomize this for
each animation frame? For each animation
frame we call update and we will redraw all the
cells over and over again. Interesting. I hope this doesn't
give someone a headache. I can also randomize
slide Y like this beautiful chaos,
Creative coding. I can also make the
image slide to the side. Higher value, I guess. Yes, move faster. Move in the opposite
direction, right? I think we've proven that
we are indeed animating. We can also make the
grid visible here.
6. Mouse interactivity: I can delete this consolo. I want to capture
mouse coordinates. I create an object here
that will hold them for me. Radius will be the interaction
area around the mouse in which the cells react to
mouse presence in some way. I take this canvas
reference from line 28 and I add mouse
move event listener. I can declare event listeners here inside the constructor. We already know this means
they will be automatically applied when we create an
instance of effect class. The only thing here is that the callback function cannot
be a regular function. It needs to be an ES
six arrow function to allow us to use this keyword that points to this
effect object and its properties from
inside this call back. Otherwise it would be undefined. Arrow functions inherit
this reference, this keyword from
the parent scope. I can consolog the
autogenerated event object. If I open it, we want the x and y coordinates
of the mouse cursor. Important thing here is
that these values are only good for us if the
canvas is full screen. Since my canvas is
not full screen, I need X and Y coordinates that account for the white
area around canvas. I will have to use offset
X and offset Y values. Instead, these
properties account for the white space between the webpage and
canvas to give us the correct mouse position
in relation to canvas. Since I used an
arrow function here, I can access domus x from inside the call back
and I can set it to do offset x. I do the same for the vertical Y coordinate I tested by consologing
these values. I move mouse over the canvas and I'm getting the coordinates.
7. How to find the distance between 2 points: Now the second most important
technique we'll use today. We want to calculate
the distance between two points
in a two D space, in this case, between
mouse and the cell. To do that, I will need
D X the distance between the mouse and this cell object
on the horizontal x axis. I will have a helper
property here. I call x velocity on
the horizontal x axis. If I do slide x plus equals dx, we get complete chaos. Let's finish this.
I also need D Y, the distance between
these two points on the vertical y axis. To calculate the distance between these two points
in a two D space, I need to calculate a hypotenus. We have this side and this side. The distance between these
two points is the hypotenus, the longest side of
the right triangle. Which means I can use
Pitagora's theorem formula, which would look like
this in Java Script. But since we are in Java
Script environment, we can also use built in
masted hypotenus method, which will give us
the same value, the distance between
mouse and this cell. I say if the distance
is less than the radius value we defined
on the mouse object. We will do some emotion physics. We will create a
helper variable. I call for example, force. It will be equal to
the ratio between the current distance
and the radius, which represents the
maximum possible distance within which the cells
will react to the mouse. I said velocity x to force
for each animation frame, we increase slide horizontal
cropping area for each cell by this V X
horizontal velocity. As I move the mouse around, it's clear we are actually detecting the distance already. I say L. If the
distance is more, make sure the cells
stop sliding. Vertical velocity will
also be equal to force. We add it here. I
define it up here. I will stop the sliding
in the L statement again. Okay, we are getting there
on the first page load, we are getting some movement in the top left corner coming
from the coordinate 00. That is because
I'm setting mouse x and y to null at first, but in this case, Javascript
sees these as 00. Even though it's better
to manually set the null, in this case I have to
set to undefined instead. Now there is no movement at first in the top left
corner that fixed it. Also, when the mouse
leaves canvas, the last known mouse position
will continue to interact. I copy this code block, turn it into mouse leave event. Here we will also set
them to undefined. Now we get animation. Only when we move
mouse over canavas. We are making great progress.
8. Getting direction from point A to point B: Now I want the image slices to expand in the
correct direction, directly way, or
towards the mouse. This is quite advanced. If you never used
math 82 before, I had to use this method for a few projects before I
understood it better. Don't worry if it's not entirely clear after
using it once. If you are an
animation beginner, you can just rewatch this part. Or you can watch
many other tutorials I made where I use
this technique. This technique is something
a creative coder uses a lot. And it will be clear
once you start using it and you play with the
values to see what they do. Built in math 8102 method will give us an
angle in radians. We can use this angle value to get an angle
between two points in a two D space and make two objects move towards
or away from each other. In this case, the
two objects are the mouse cursor and the cell containing the
slice of the image. We are already
calculating DX and DY, horizontal and vertical distance between the mouse and the cell. Method 82 expects
these arguments to give us the directional
angle between them. It expects y first
and D x second. It's important to remember, it's just how this
built in method works. Method 82 gives us an angle between two
points, mouse and cell. In this case, it
returns the angle in radians between the
positive x axis. And a ray from 00
towards a certain point. The way we use this, the
ray goes from object one from the cell towards
X and Y of the mouse. Keep in mind that mouse can be to the right or to
the left of the cell, which means we can get
positive or negative values. The value range is between plus pi and minus pi Using mouse first or second here in this formula will result in
a different set of values. The resulting angle values
will be organized differently, but in creative coding
we don't really mind because once we
get the animation, if the objects move
towards each other, we can swap their direction by multiplying the
values by minus one. What I'm saying is we can
adjust the code later to whatever values method
e two is given us. I will show you to recap. For our purposes, method
e two takes DY and dx, the distance between two points on vertical, horizontal axis. Based on these two values, it will give us an angle
between minus pi and plus pi. From that angle, we know in
which direction we want to push the objects to make them move towards or away
from each other. The range of values
between minus pi and plus pi covers the entire
circular area. Because full circle is two pi, 360 degrees, this means we are covering all the possible
directions on a two D plane. I know this is a lot
of math for beginners, don't feel the pressure to fully understand all of
this at this point. Practice will make it clear once you understand
this technique, you can make so many
really cool effects. It's worth the effort. Okay, now we will pass this resulting angle value
to sine and cosine methods, which will map their position
along a circular area. Sine and cosine can work
together if they both get the same angle input to create a circular motion
or wave emotion, depending on how we use them. Since sine and cosine convert these angle values between a range of minus
one to plus one, we would barely see any motion at all With these small values, we are multiplying that
small directional vector by force increasing its radius. We know that force is
directly related to the ratio between
the current distance between the two objects, mouse and cell, and their
maximum possible distance. This will add a lot of nice
physics into our project, and the motion will look
smooth and natural. We are at the point where I
check what motion we get. Then I will just adjust the values to get
the motion I want. I synchronize sine and cosine output by
swapping them around, we get this motion instead. This is how you can get circular motion radiating
away from the center point. In this case, our center
point is the mouse cursor. I have this L statement
here to make the image slide back into place when
the mouse moves away. But to get a completely
smooth motion where the image pieces
slide back and forth. Ideally, the entire thing should be calculated as a single value. And then we apply
that final value to slide x and
slide y properties. This will give us
much better result, but how do we do it?
9. Motion physics formula (friction & easing): I want to have a
calculation here that is a result of
two competing forces. The first part here
is the push away from the mouse if the mouse and the cell are close
enough to each other. The second portion of
this calculation will be a force that slides the images back to
their original place. Since both of these forces will be a part of
a single formula, we will avoid getting
any motion back and forth because only
a single value will be calculated
here in the end. Let's expand this
formula and explain it. The first part is the force
displacing the pieces. And the second part will
be trying to put them back to where they originally were
to their initial positions. Now they slide back too fast. Keep in mind this
entire update method runs 60 times per
second, over and over. Recalculating these values
for each animation frame. I don't want to push the
slide all the way back, but only by a fraction of
the difference between the current position and the
original default position. I multiplied by easing to create these individual steps
in between like this. I want them to slowly ease
back to where they were. I try different values and
I see what happens now. At the same time, I
want the push force to decay over time, getting
gradually weaker. In the real world of physics, we call this force friction. For each animation frame, I want the force to have
only 80% of the push power. Meaning that it
will gradually get weaker and weaker and
it will eventually get overpowered the second half of the formula that is
working against it, trying to slide the cell back to its original
place by a fraction of the difference between its current position and its
initial original position. I do the same for the
vertical y coordinate. We slide the cropping area of each cell by applying two
competing forces on it. These forces work
against each other, but they are both a part
of a single formula. We get a smooth motion. As a result, if the
distance between cell and the mouse is less than
radius value we predefined, we are calculated vertical
and horizontal force pushing the slides
away from the mouse. That push force is applied
to slide x and slide Y values as the first
part of the formula. And that push force decays for each animation
frame by friction, the speed of that
decay can be modified by giving this friction
a different value. Then we are applying a force
in the opposite direction. If there is a plus here, we need a minus here. And that opposite
force is a fraction of the difference
between the particles original and the
current position. As the push force slowly
decays and the animation runs, eventually this right
side of the formula take over and slides will ease back
into their original place. I encourage you to play
with all these values. Swap, sine and cosine, swap plus and minus, give friction and ease
different values. You can also apply this formula to x and y of the cells instead, to get a completely
different effect. After you run your
own experiments and randomly come up with
cool and unique effects, these calculations will
make even more sense. I can comment out the
rectangles we are drawing because this will make the effect more performant. Drawing images on
canvas is cheap, but drawing shapes is expensive. Even rectangles that are
one of the simplest shapes, the value we give
to ease will define how fast the pieces
slide back into place. The value we give to
friction will define how fast the push force
decays per animation frame. Friction always needs to be less than one and
more than zero. If you give it more than one, then the push force
will just compound endlessly and the image will never reassemble
itself again. Not only these
values themselves, but also the ratio between these two values will
affect the motion. Now the push force decays only by 1,000 per animation frame, which means it stays
high for a long time. Dot is basically
slicing the distance between the base
original position and the current position into multiple pieces
and move it back towards that original
position one slice at a time. If this dots is 0.01 we move towards the
original position by 100 of the distance between the original and the current
position per animation frame. But as the push force the case by 10% per animation frame, it will eventually allow the cells to slide
back into place. I'm not sure if my
explanations at the end helped or made
it more confusing. The best way is to play
with the values yourself, see what motion you get, and it will start making sense. Now that we covered
this technique, you are ready for more advanced
version of this effect, where we turn each
pixel in the image into a particle and we apply similar
physics formula to them. We can also increase the number of cells that make up the image. The more cells, the
more performance demanding the effect will be. Of course, right now, I'm asking Javascript to draw 3,500 images 60
times per second, and slide their cropping areas around calculating motion
physics for each cell. It still runs pretty
smooth on my computer, but I probably shouldn't
use so many cells. It just shows how good canvas
is at rendering images.
10. Moving the cells around: Okay, this was the beginner friendly version of the class. I'm coming back to give you
some quick fire experiments. I delete this consologe, we have these four
variables to move the cropping area
inside each cell, and to slide it around. I will create four
more variables. And these four will move the cell position X and position Y for horizontal and vertical
position of each cell. And speed X and speed
Y for their movement. Speed, slide X and slide Y are applied here to source
X and source Y. Arguments passed to
draw image method, they determine the position
of the crop in area. We are sliding that crop
in area using VX and VY. Now I take position X and
position Y a separate set of variables and I apply them to destination X and
destination Y arguments. This area will be for
the crop effects, this area will be for cell
positions and movement. They will work nicely together. If we do this, well,
let's see how it goes. Speed X will be the
final target position minus the starting position. We will do easing again. We will slice that
journey into ten pieces. And we will move cell
towards its target position by one tenth of the distance
per animation frame. Speed Y is also the
target position minus the starting position divided
by some ease in value. Now I can take these values and apply them to position
X and position Y, since we are using
those here inside draw image as destination
X and destination Y. This will actually move the
cells themselves like this. They all move at the same time, so it just scales like this. We can make this much more interesting in many
different ways. Let me show you some of them. For example, each cell will have a randomized value
2-12 And we will ease each cell by its own
randomized ease in value 2.52 we can change the
starting positions here. The cells will fly out
of different points, different corners of canvas, for example, Depending
on what we do here. Bottom right corner,
bottom middle. Nice, I told you, we will
make this more interesting. Notice that the
images still react to mouse by sliding
the cropping area.
11. Performance optimizations: I can also stroke them, but I have to use position
X and position Y here. Yes, this is so cool. Stroking these rectangles
affects performance. Let's reduce the
number of cells here. This still looks great,
don't you think? Let's try to find
a good cell size. The only problem is that this code block
runs all the time. I want to save some
performance and stop calculating these
small pix values. The cells are close enough to their original target
positions at the point when the image has
been assembled or at least almost
assembled close enough. I say only if speed
x is more than 0.1 or speed Y is more than 0.1
Only then move them around. Otherwise, we assume they
already assembled the image. These calculations
are not needed. Now, we don't have any
speed x and speed Y. We need to set them when
the cell is created. Let's give each cell a
custom start method. Here we will set the speeds. We will automatically call this start method
from the constructor. Problem is that cells can be
to the left or to the right. Speeds can be
negative or positive. Basically, I want to
run this code only when the speeds are
less than -0.1 and more than plus 0.1 Which
means the cells are far enough from their
target positions and I want them to move. Let's just remove the minus. By wrapping these
in method absolute, like this, it will return
the absolute number. Okay, this code block is
moving each cell from its starting position
towards its target position. Once the cells are close enough and the image has
been assembled, we don't run this code block
anymore to safe performance.
12. Creative coding experiments: I want to test if
this code block actually stops executing. We were passing cells index
value from the for each loop. Here I want each cell to have a unique index value that increases by one for
each new cell we create. I pass that value to
cell class constructor. As the fourth argument, I make sure it's expected here and it's converted
to a class property. I will consolo the index
of each cell here. I want to make sure
all of them stop running this calculation
at some point when the cells are close enough to their target positions and
the image has been assembled, the limit of how
close we want them to be can be changed here and here. Yes, we can see all the indexes stop being locked at some point. Consolo like this will
slow your project down. We need to make sure we remove
it when we don't need it. We can do so many other things. Each cell has a
unique index value. We call start method automatically when
we create each cell. We could also delay them
by putting set time out. Here we pass it
callback function. How long to wait
before this runs? I don't want to delay all of
them by 500 milliseconds. Each one will be delayed
by its own index value. Callback will run
the start method and this will give us an error. The reason is this keyword, it becomes undefined in the
scope of this call back. We already faced this
issue with callbacks and this keyword here,
inside event listeners. We know there is a
simple solution because arrow functions inherit
this from the parent scope. We turn the call back
into an arrow function, and here we go, we
have a custom delay. We can increase that delay
by multiplying index here cells cropping area
still slides around. While all these
things are happening, nice, now I can change the starting
positions here to get a set of different
interesting effects. How about the middle? Instead of pushing the
cells into the array, I will unshift them, fill in the cells array
from the other end, and this will
result in the cells coming from the back of
the image like this. If you are experiencing
performance issues, I recommend removing the
stroke rectangle coal from line 32 or you
can draw less cells. By adjusting cell with cell height properties
on the effect glass, we can make them come from
the top middle. Like this. We can also randomize their starting X and
get a different effect. How about random X and bottom Y? We can also clear
old paint between every animation frame to
get an effect like this. How about random X and random Y0x and
random Y negative X, and random Y0x and zero y? They all move at a
consistent ease. Then I can get more
organized patterns by adjusting these two values. I gave you so many more
values you can tweak. Now it's time for
you to take what we learned and come up with
your own unique combination. We have a complete control and we understand how
this code works. If you are unclear
on some of these, play with the values
and watch what happens. It will start making more sense. Eventually, we can take
this to a whole new level. By making each cell just a
small pixel in the image, we can have images that are
made out of many more pieces. Turn images into
particle effects and even particle rain or
particle fire, for example. If you are ready to take your
images to the next level, I'll see you in the next part.
13. Project 2: Particle Images: Javascript is a powerful tool. Let's explore the secrets of drawing and animation
techniques in modern front end
web development. In this class, we
will start by drawing a simple rectangle and
image on HTML canavas. We will discover how to turn these rectangles into
a particle system. It will be a smart
particle system that can recreate and remember
shapes based on pixel data. From any image, I will
show you how to give each particle physics such
as friction and easing, and we will add some
mouse interactions where we can break
the image into individual pixels and it will automatically put
itself back together. Come join me and let's
learn how to turn images into interactive
digital artworks.
14. Project 2 setup: We start by creating
three files, index HTML, style
CSS and script GS. I open index HTML. In my internet browser, I will be using Google Chrome. Inside index HTML, I create
a basic web page markup. I'm using visual studio code
editor with met extension, so I can just press
exclamation mark and tab key and I get a basic web
page boilerplate like this. This video is a HTML
canvas introduction. But I expect that you know,
some basics of Java script. Give my web page some title. For example, awesome
Javascript Effects. I link my stasis
as file like this. I create HTML five
canvas element with an idea of canvas one. Html canvas element is used to draw graphics
on a web page. We can draw static shapes, as well as dynamic
interactive animations that react to user input, which is what we will do today. The canvas element is a
container for our artworks, it's our art board. We can use Javascript
to draw shapes, lines, text, or images on it. It might sound very basic, but once you understand
how canvas works, you can do an infinite
variety of digital art, interactive games,
and web graphics. This course, we will explore what can be done with images and we will go from basics to some really cool
advanced effects. Hop I create a diff element
with a class of controls, and inside we will have
a button with an idea of Warp button and text
that says Warp. When we click it, it
will break image into individual particles
and the image will reassemble itself again. Automatically, we will
be able to control the speed and the weight
of particles as they move. This project will also
contain some two D physics. Don't worry, it's not that
complicated if you take it step by step with me
as we write the code. We will also link script G
file down here at the bottom. To link our Java script
logic in stysSss, I start with global
reset to make sure our page appears the same
across all different browsers. I use asterisk selector to target all
elements on the page, and I set margin to zero. And padding to zero canvass
with an ID of canvass, one will have a blue background. This is just temporary
so that I can see where it is on
the page when I'm positioning it with a class of controls will be set
to position absolute. This will remove it
from the document flow and place it over
the canvass element. We need to make sure that
the buttons it will contain are not covered by anything
and are always clickable. So I set its z index to 100, putting it all the way up top in front of all the
other elements.
15. Converting images to code: I go back to index HTML and
I create an image element. I give it an ID of image with
the source of the image. We will have to do
a little trick. I have my image files
ready on my desktop. It's just a normal image in PNG format with a
transparent background. If I just put this image
into my project folder and I point image source attribute to it like we would normally, we will be able to draw
that image on canvas, but we won't be able
to analyze it for pixel data and break
it into particles. If we try to do that, we
will get an error that says canvas was tainted
by cross origin data. This error should only happen when you run your code locally. Once you upload the project
to an on line server, the PNG file in the source would be considered
the same origin. But we want this project
to work at all times. We don't want to
trigger any errors even when we work
locally like this. To bypass tainted
canvas warning, we will convert the
image itself into code. We assign that code
as a source here. And that way the image itself will be part of index HTML file. Therefore, it will be considered same origin in all
circumstances, most people don't know that
images on the web can be broken into a single
very long line of code. We call this a base 64 string. That line of code contains
all the image data. Completely replacing the
PNG file we have here. I can convert my image into base 64 format by
drawing the image on canvas and then calling built in two data URL method
on canvas element, turning the entire canvas with that image drawn on it
into a data screen. Today, we will use
even simpler option to do that and we will use one of many
available websites that will do it quickly for us. I open another
browser window and I Google phrase PNG two base 64. I select the first
link from online Png tools.com If you want to use
the same images I'm using, you can download them in the
video description images are enemies from Steampunk Fish game tutorial I made recently. I can simply take the
image and drag and drop it here on the
right hand side. It generates base
64 code for me. For the code to work,
I need to make sure the string starts with
the data colon image, PNG base 64, like this. If it doesn't start
like that for you, just make sure you
take this checkbox. We took a PNG image file and we converted the entire image
into a line of code. This string now contains all pixel positions
and color values, completely replacing
the image file itself. Any internet browser
can understand this string and draw
the image from it. You want to learn how to use the other method and
generate this string dynamically with
Javascript without using an external website. I do that in my other course, where we generate
complex fractal shapes and make them into
falling snowflakes. I highlight this very long base 64 string and I copy
it in index HTML. I place my mouse courser
between the quotes on SRC attribute and I paste
all of that inside. If I save the changes we
are drawing the image, you can see base 64 string
contains the entire image. No need for the actual PNG file. This way we can
manipulate it with Javascript in any way we want. You can click View Word Wrap to toggle between a view where
it breaks lines like this, we can see all the code. And another view where the whole thing is on
one line like this, you can see there
is a lot of code. Years ago, I released a simple
version of this algorithm. And it would only work
with small images, 100 times 100 pixels. Anything bigger than that would become very slow and laggy. This version works even with larger images because
we will control how many particles the image breaks into with Java script. This code base has many
improvements and optimizations. You will see the
bigger image you use, the longer base 64
string you will get. Instead of pointing image
source directly to a PNG file, we converted our
Angular Fish image into a base 64 string. This will allow us to
analyze the image for pixel data and do all kinds of cool effects and
animations on it without having to deal with
any cross origin errors. I don't really want to draw the image element
on my web page. I want to draw that image
on my blue canvas element. I will take IMG tag and
I give it a display, none, the image is hidden. But we can still access it with Java Script to draw it on
canvas when we need it. It's still sitting
here in index HGML.
16. How to use canvas: In script GS, I create an
event listener for load event. All the code in this
project will be written inside this
event listener. Load event will make sure
that the entire website, including all
dependent resources such as style sheets and images, is fully loaded and available before we run any
Javascript code. When working with
Javascript code that depends on an image, it's a common error that happened to me many
times that I didn't wait for the image to be loaded and I was getting blank canvas. Javascript code gets
executed very fast. Images can take a couple
more milliseconds to load. We can't perceive that
difference by naked eye, but if Javascript code runs
before the image is loaded, we will get blank canvas
load Event listener is one of the ways we can
deal with this delay. We start with a
simple canvas set up. I create a constant variable. I call for example canvas. And I pointed towards the HTML canvas element we
created in index HTML file. By using get element by ID, we gave it an ID of canvas one. Like this variable I call CTX. Shortcut for context is this canvas variable from
line two get context. Context is a special method
that can only be called on a variable that holds
a reference to HTML. Five canvas element get context method
expects an argument, we call context type, we can pass it two D or web GL. Today we will work
with two D when called like this get context method will create an instance of an object called canvas Rendering Context
two D. This object contains all canvas
settings and all built in canvas drawing methods
we will need today. I can consoloc CTX
to have a look at it. I can see it right here. If we open it, we can see all the default canvas settings, so called canvas state. You can see, for example, that the default fill style
is set to block here, default font is ten pixels, san serif, that default
line width is one pixel. We can override any of these
settings by assigning them to a different value
with Java script, and we will do that later. If I click on the prototype
here and expand it, we can see all the
built in two D methods. We can use these methods to draw rectangles, circles, and lines. We can create gradients
and manipulate images. Today, we will cover the basic
ones and we will go really deep on draw image method
that sits right here. I will also show
you how to analyze an image pixel by
pixel and use that for some cool animation
magic with get image data method which
is coming from here. Let's delete the consoloc and
close the browser console. I take canvas variable from line two and access its
width property. I set it to window
in width to make canvas cover the entire
browser window horizontally. I also do the same
thing with height to make it cover the
screen vertically.
17. JavaScript classes and particle systems: Today I want to show you how to create a so called
particle system. Generally speaking,
particle system is a collection
of small objects. Each particle is a small
graphical element. It can be an image or a shape. We can program these particles
to look and behave in a certain way to simulate all
different kinds of effects. It could be fire,
bouncing balls, swarms of enemies in a
game, or many other things. Today, we will make each
particle into a pixel in our image and we will break
them into individual pieces. At the same time, these
particles will react to mouse in a nice dynamic way with
physics and friction involved. To create all these particles, we will use a custom
Javascript class. I call it for example, particle like this
with a capital P. Javascript classes
are blueprints to create many similar objects. Javascript is a prototype
based language. Every Javascript object has an internal property
called prototype that can be used to extend object
properties and methods. Classes in Javascript are so
called syntactical sugar. It's a cleaner and more
elegant syntax built over native Javascript prototype
based inheritance that mimics classes from
other programming languages. Simply said, class
is a template. Every time we call
this particle class, it will create one new
particle object for us. We will also have a class
I will call Effect. This class will handle
the entire effect, all the particles at once. The last piece
we'll need here is Animation Loop Particle class as a blueprint to create
individual particle objects, effect class to handle all
particles at the same time. And animation loop To make it all animated and interactive, I will give my particle
class constructor method. This is a special
method when I call my particle class later with the new keyword
constructor will run all the code inside of it to
create one new blank object and assign it values and properties based on
the blueprint inside. Let's define that blueprint. Let's define the properties
of our particles. Every particle will be a
piece of an image, a pixel. It will need to sit at a very specific position and all the particles combined
will make up the whole image. That means it will need x and y coordinates so that
Java script knows where on canvas to draw
it on HTML Canvas, same as in web
development in general. We have horizontal x
axis going from zero pixels on the left and
increasing as we move right. We also have a vertical
Y axis starting at zero pixels up top and
increasing as we go down. For example, position 5,100
on canvas will be here 50 pixels from the left
on the x axis and 100 pixels from the
top on the y axis. Initially, I will set x
and y two coordinates, 00 for top left
corner on canvas. When we define properties
on Java class, we say this x, which means x property. On this instance of particle class the
constructor is creating, right now our particles will be rectangles because
Canvas is faster at drawing rectangles
rather than circles. In the previous version of
this effect, I used circles. This time we will be able to draw more particles efficiently. Size of each particle rectangle will be three times
three pixels.
18. Drawing rectangles: To draw a rectangle on canvas, all we have to do is take CTX variable from line three
that holds an instance of canvas two D join API and we call built in phil
rectangle method like this. It expects four arguments x and y position
where to draw it and with height of the rectangle it will fill
that rectangle with color. If we don't define fill style, we know that default style
on canvas element is black. Here is our black
rectangle drawn 120 pixels from the left on the x axis and 150 pixels
from the top on the y axis, 100 pixels wide and
200 pixels tall. If this is your first
canvas project, you should maybe post
the video and change these values so you
understand how rectangles, same as images on canvas, are drawn from the
coordinates we give it. And they go to the right
and down from that point, depending on their
width and height. We can also draw circles, curves or lines and make
different shapes and animations. With these, you can draw
pretty much anything. Once you know how canvas works, if you are interested in art
that can be done with lines, check out my class on fractals. Today we are going
deep on images. Let's bring our angle fish image into the project and
draw it on canvas.
19. Drawing images: I create a constant variable, I call image one. I pointed towards
the image element using its ID image one, which I gave it here. Now I can take CTX variable from line two which
as we said holds all canvas properties
and methods I call built in draw
image method like this, draw image method expects
at least three arguments. The image we want to draw and x and y coordinates.
Where to draw it? I pass it image one from line
seven and I want to draw the image from the
top left corner of canvas from coordinates 00. I give it position of x 100. We push the image on 100
pixels to the right. If I pass it on 100 pixels
as vertical y coordinate, I push the image on
100 pixels down. I can also pass it optional
with and height arguments. If we don't pass
with and height, Javascript will just draw the
image at its original size. If I pass it width of 100 pixels and height
of 100 pixels, we squeeze the image
into an area of 100 times 100 pixels
and we distort it. I can stretch and squeeze
the image like this, which could be useful
for certain effects. If this is your first time
using draw image method, you can post the
video and pass it different values
for x Y with and height so that you
can see how it affects the position
and size of the image.
20. Drawing particle objects: Now we know how to set
up a Htimlcnvas project. We know how to use fill
rectangle method to draw a simple rectangle
and how to use a draw image method to
draw an image on canvas. Let's use our particle
class to draw something. I delete this code.
Let's say I want each particle to be black
square of 30 times 30 pixels. I give my particle
class a custom method. I call, for example, draw. Its job will be to
take properties from class constructor and draw particle of that size
at those coordinates. Let's start simple and
then we refactor it to employ good practices of
object oriented programming. Again, to draw something, I take CTX variable
from line three. From it, I call built in
Phil Rectangle method. We already learned that Phil Rectangle expects
four arguments x, y, width, and height. That Java script
knows where to draw that rectangle and what
size it should be. I pass it this x from line 11. This dot y from line 12
as x and y coordinates. I pass it didot
size from line 13. As with the same
disdote size property as height like this. We define our particle class. We have a blueprint with
all the properties. And we have a draw
method where we take those properties and use
them to draw a rectangle. How do we actually
run this code? To draw it, we need to create
an instance of this class. I create a constant variable, I call particle one. I set it equal to new
particle like this. The new keyword is a special
command in Java script. It will look for a
class with that name. It will find that
class here on line nine and it will automatically
trigger its constructor. Method constructor will create one new blank object
and it will assign it properties based on
the blueprint inside. Because this particle one object was created using
this particle class, it has access to draw method
we defined on line 15. We can simply call it like
this, particle one draw. Nice. We are using our class to create and draw one particle. I can adjust its X and Y
position here to move it around. I can also change its size.
21. Effect class: If I want to create
more than one particle, I can just repeat this code and create variables
for particle two, particle three, and so on, but that would not
be very efficient. Our code should be dry, which is an acronym for
don't repeat yourself. We are also trying
to keep everything in object oriented
code structure. We can use this effect
class we defined on line 20 to create and handle
multiple particles. Particles will handle
individual particles. Effect class will handle
the entire effect, all the particles at once. I give it a
constructor this time. The constructor will
expect two arguments for width and height to
come from the outside. This will make sure that
the effect is aware of the current canvas
dimensions Inside I convert these arguments
into class properties. I'm saying take the
width that was passed as the first argument to the class constructor and
convert it to width property. On this particular instance
of effect class you are creating right
now, same for height. We will also have
particles array property. Initially, I set it
to an empty array, it will contain all currently
active particle objects created by particle class. From line nine, I will give my effect class
a custom method I call, for example in it, its job will be to initialize the effect and fill particles
array from line 24 with many particle objects. Let's start with
just one particle. I take this particles array and I call built in
push method on it. The push method adds one or more elements to
the end of the array. I want to push one particle in, so I pass it new
particle like this. The new keyword will
jump to line nine and it will trigger
particle class constructor creating one new particle object based on this blueprint.
I delete this code. We will draw shapes from
inside our effect class. Now to draw this one
particle we are holding inside particles array
I create draw method. This method will take
particles array from line 24 and it will call for each
built in array method on it. The for each method executes a provided function once
for each array element. I will use modern
ES six in tax here. This is so called
arrow function. Now particles array
contains only one particle, but it can also contain many. I'm saying take that
particles array and call for each on it. Assign each object in the array a temporary variable
name particle on each of those call their
associated draw method. From line 15 we have
effect class with init method to fill
particles array with particle objects and the draw
method to draw all of them. This draw method on line 29 draws all the particles,
the entire effect. This other draw method on line 15 specifies what will
each particle look like. In our case, we decided to draw them as simple black rectangles. For now to draw the particles, now I create a
constant variable. I call effect, and I set it to an instance of my
effect class like this. On line 21, I can see that constructor expects arguments
for width and height. I pass it canvas width from line four and canvas
height from line five. Let's consolo this effect variable to check if
everything works so far. Nice. I can see my effect
object and it has width and height properties and also particles array that
is currently empty. I take my effect variable and I call it method from
line 26 like this. This method should push
one particle in there. I check in console, yes, we have one particle object inside and I can see it has x, y, and size properties that
all have correct values. If you see undefined
in some of these, it means there's a mistake
somewhere in your code. It's good to check from time to time as you write your code, if all your properties
have values, especially if you run
into some problems or errors while coding consoloc
is here to help us. Now I can take effect
variable again and I call draw
method from line 29. I also delete the consoloc. Nice, we are drawing one particle using our
new code structure. I can move the particle around
by changing these values.
22. Code cleanup: We are doing something bad. Here we are pulling CTX variable from line three directly
into our class. We want our classes and
objects to be modular, self contained as
much as possible and independent from
their surrounding code, from their lexical environment. Rather than pulling
CTX directly, I will convert it into a
variable and pass it along. I will make sure draw method on particle class expects
context as an argument. I will then use that
context variable here to call phil
rectangle method from it. I will pass tx variable from line three as an
argument to draw method. When we call it on the
effect class on line 36 we give it a variable
name context here like this. We pass that reference along to draw method on
particle class. That reference will
be passed here. From there we call
Phil Rectangle. Doing it like this will make
sure our code is modular. It's considered a good practice rather than what we had before.
23. Multiple randomized particles: We can also randomize
particle positions. Rather than hard coding
the coordinates, I can set horizontal
x position to a random number between
zero and canvas weight. Vertical Y position could be a value between zero
and canvas height. Now every time I
refresh my page, particle will be at a
different random position somewhere on canvas. I could also randomize
the size if I want to. Inside it method, I create two particles by
copying this line. Nice, Now we have two particles as we create them
using the new keyword. Every time the constructor
runs on line ten, it will assign different random x and y
position to each one. I can also create three
particles like this, same as we did
with CTX variable. Here we are pulling
canvas width and canvas height directly
into our class, which makes this class dependent
on its surrounding code. We are already converting canvas width and canvas height
two class properties on effect class here by passing it to effect class
constructor on line 36. Let's remove these
two particles to make sure each particle is aware of the current
canvas size. I make sure particle
class constructor expects a reference to
the entire effect class, entire thing as an
argument inside I convert that reference to a class property called
this dot effect. Keep in mind that objects in Java script are so called
reference data types. Which means that by
passing a reference to the entire effect object
to this particle object, I'm not creating a copy of effect class each time I
create a new particle. This dot effect is not a copy, it's just a reference
pointing to a space in memory where the main
effect class is stored. It has many advantages. The main one is that when we update any values
on effect class, those changes will be
immediately visible from this dot effect property
on particle objects. Now this dot effect points towards this
entire effect class, and we can access any properties on it from inside
particle class. Because of that, I can replace canvas width by dist
effect dot width, referencing the
value from line 23. As we know, this value
represents canvas width. We can also do that
here on line 13 and use dist effect dot height, a value coming from line 24. If you are a beginner
passing values around like this might take some time to fully understand. Don't worry about too much. If you are struggling,
you will become good at this as you write more
object oriented code basis. Now that we know that
particle class expects an argument pointing towards
the main effect class. Here on line 28, where I create an instance
of particle class, I pass it effect as an argument, a reference to all of
this, this entire class. However, since we are
inside that effect class, I need to use this
keyword instead. This keyword used inside this class represents
the entire class itself. When I refresh the page, I have one particle
being randomly generated and drawn
somewhere on canvas. Nice. We are passing canvas width to the
effect class on line 35, converting it to this thought
width property on line 23, and using that value on line 12 to calculate
particle position. We are also doing
the same for height. Now I can copy this line again
to create many particles. But what if I want
100 particles? Copying this line of code
would become impractical. This is a perfect scenario
to use a four loop. I delete all this code, and instead I will wrap
this in a four loop. Like this. Index
will start at zero, as long as index
is less than ten, increase index by one
and create one particle. This four loop will run ten times and create ten particles. Four loops are great to help
us reduce code repetition.
24. drawImage method: Let's also draw an image. But because I don't want
to be pulling this image one variable into my
classes from the outside. Let me just turn it into a class property on the
effect class like this. I'll call it Did image. We can draw it by taking context and calling
draw image on it. I pass it dot image from line 24.00 for x and y coordinates. We are drawing our
fish on canvas again. But this time we
are doing it from a self contained object
oriented code base.
25. How to center images on canvas: Images on canvas are drawn
from top left corner, going right and down. Let me show you how to center an image exactly in
the middle of canavas. I will create a helper
variable called this center x. It will be this dot
width from line 21. So the width of canavas times 0.5 middle of canvas
horizontally, dot center Y is dot
height from line 22 times 0.5 the middle
of canvas vertically. I can use these new
properties as x and y coordinates passed
to draw image method. Now the image is drawn from the point on canvas that
is exactly in the middle. However, the image
is not centered. Dot image is a reference to IMG element we created
in index HTML. Let me consologate
This image element has automatically generated
width and height properties. If I consolog dot image
width, you can see it. It says 500 pixels dot image
dot height is 369 pixels. Now that we know this, we
can delete the consoloc. And we can use width and
height of the image to offset position by half of image width
and half of image height. To center it, I create a property called dot
x on effect class. In this case, distotex means horizontal position
of the fish image. It will be dot center
x from line 25, so the middle of canvas area horizontally minus
half of image width. Now I can pass didot x to
draw image method on line 37. And the image is
centered horizontally. Let's do the same for
vertical Y position dot center Y minus
Sdt image dot height times 0.5 I replace it here. To center any image on canvas, just find the middle of canvas vertically and horizontally, and offset those
values by half of image width and half
of image height.
26. Particle motion: So we are drawing our fish as an image and we are
drawing ten particles. Let's make it 100 particles. Let's make each
particle a square, five times five pixels. You can play with the size. Whenever I refresh my page, all these hundred particles get a random position
somewhere on canvas. We can also make particle
size random, like this. We can also make
these particles move. We will handle that logic here. Inside update
method, I will need a new property called
V x velocity x, which is basically
a horizontal speed. Let's set it to 11 pixel
per animation frame. For now, I also create velocity y and I set it to
plus one as well. Inside update method for
each animation frame, we want to increase
horizontal position from line ten by the value of
Vx from line 13. We also want to increase
position Y by the value of V Y. Each particle will have its own update method that
will define its movement. I also create update
method on effect class. This method's job will
be to call update on all currently active
particle objects inside. I take particles array. For each particle
object in the array, I call its update method, which is created on
line 19 like this. If I call update method like
this, not much will happen. All particles are just drawn once and their properties
get updated once. There is nothing Calling the update and draw
methods over and over, we need animation loop. I take these two lines
of code and I put them inside this custom
function I called animate. I call request animation
frame built in method. It sits on the window object, but it can be called directly. Like this request animation frame method will
call a function we pass to it as an argument before the next browser repaint. When I pass it animated the
name of its parent function. It will constantly draw all particles and update
them over and over, creating the animation with
request animation frame. The number of call backs is
usually 60 frames per second. It will generally try to match the display refresh rate
in most modern browsers. It will also automatically pause the animation
when running in background tabs to improve performance and battery
life of the computer. When I call animate and
run my code, we get this. Our black rectangles are
moving and leaving trails. The trails are the
old animation frames. If we want to see only the
current animation frame, we have to delete canvas every time we update
particle positions. I do that by using built
in clear rectangle method. I want to clear canvas
from Corin at 00. I want to clear
the entire canvas. I take values from up here. The width of the cleared
area will be canvas width. Height will be canvas height. Now we are animating. Congratulations, you
are a creative coder. You just built a working
particle system. Particles are moving because
we are adding velocity x and velocity y to their current
x and y coordinates. If we want each particle to
move at a different speed, I can assign them a random
speed from a predefined range when they are first created here inside particle
class constructor, I can, for example, set their speeds to be
a random number 0-10 That's very fast. If you want the particles to move in all possible directions, not just right and down, we can push the speed range to start from negative numbers. This line means a random number between minus one and plus one. Particles with minus
value will move left. Particles with a plus
value here will move to the right for vertical y
coordinate. It's the same. Particles with a minus
value here will move up. Particles with a
plus value will move down since each particle
will be assigned a different random
velocity x and velocity y at the point when they are first created by
the constructor. Here the ratio between V, x and y will give each particle a
different direction and speed of movement. Each particle will have
a different vector. Let's remove the
blue background and leave canvas background
transparent. How do I make my particles
take the shape and color of the image and how
do we make them remember that shape
and recreate it? If we break it with mouse, we are about to learn
that right now. This concludes the first
part of the course where we covered the basics
of HTML canvas. We learned how to make a simple basic particle system
and how to handle, draw, and center
images on canvas. The second part of
this course will be more advanced and
we will learn more about image manipulation and
particle physics in depth. Congratulations, If
you are a beginner and you follow the far,
you are doing great. If the code in the
following lessons feels more challenging,
don't worry. In this next part,
we are entering advanced creative
coding territory. But I think you can do it. We
will take it step by step, and I will do my best to help
you understand. Let's go.
27. Pixel analysis with getImageData: This is our custom
particle class. Its job is to create one new particle object every time we call it. With
the new keyword. Right now, our particles look
like small black squares. I actually want to pixelate
the image and make each pixel block a particle
of a specific color. That particle also needs to
remember where it sits in the overall image because
we want to be able to push them around and do
other tricks with them, and we always want them
to find the way back to their original position
to recreate the image. Here on line 36, we have
initialized method just simply pushes 100 random
particles into particles array. I comment this out,
we need to find another way to turn our
image into particles. Also keep in mind that distote x from line 33 and distote y from line 34 define horizontal
and vertical position of the fish image we
are drawing on canvas. I can prove that by
increasing diste x by one for each
animation frame here. Which will make the fish move to the right on the
positive direction. On horizontal x axis. I delete the code from init method and I put
draw image inside. The job of this
initialized method is to draw image on Canvas, analyze it, and turn
it into particles. Then we delete that image
from Canvas completely, because we will
have our particles making up and recreating
the image itself. Let's do it step by step. Since we are drawing
something from inside in it, we need to make sure it expects a drawing context
as an argument. I call it on line 48 down here. And I need to make
sure I pass it that context CTX variable
from line three. It gets called only once on
the first page load Here, Animate will immediately
clear the canvas and delete the
image for a moment. I will comment out animate
to stop it from running, let's focus on
initialized method. Here is our custom
particle class that creates our particle
with these properties. Then it draws them
as black squares and makes them move,
updating their positions. We will use this particle
class and we will make it turn each pixel in the
image into one particle. Inside it method I create a temporary helper
variable called pixels. This will be an array containing
all the positions and color values about each
individual pixel on Cannavas. We can't analyze image itself. We need to draw the image on canvas as we are doing
here on line 37. And then we use get
image data to analyze our canvas element with
the image drawn on it. Get image data method analyzes a specific
portion of canvas and returns its pixel data in the form of a special
image data object. It needs four arguments to specify which part of
canavas we want to analyze. I want to analyze the
entire canvas element from coordinate 00
to the width of canavasdizotwidth from line 27 and the height of canvas
height from line 28. The important thing to mention here is that get image data some built in security measures when we run the code locally, as we are doing in this
project right now. If we draw an image
file and then try to analyze that canvas
with that image drawn on it, we will get a console
error that says canvas was tainted by
cross origin data. Even if you put
the image file in the same local folder
as your script file, it will still be
considered cross origin. That's why we did the whole
thing in the beginning, where I converted image
file into a base 64 string. Doing that, the image itself becomes a part of
index HML file. We can avoid this
tainted canvas warning. Let's consolok pixels to see image data object in console. I can see that get image data correctly returned
image data object. Inside this data property, it contains a massive
array representing color values of each pixel in
the entire canvas element. This is a special type of array, so called UI NT
eight clamped array. It's an array of unassigned
eight bit integers clamped to arrange 0-255 If you worked
with web colors before, you know that every color
can be expressed by RGB and alpha value called
RGBA color declaration. We use it in CSS all the time. In this case, red, green, and blue can be any
value 0-255 In CSS, alpha opacity is a value
0-1 here in this array, alpha is also a range 0-255 where zero is transparent
and 255 is fully visible. This array is organized
in a special way, where each four
elements represent red, green, blue, and
alpha of one pixel. Four elements in this
array are one pixel. This is important to remember. I just opened the beginning
of the array and canvas gets analyzed by kit image data
from the top left corner. That means that this top left
corner pixel has zero red, zero green, zero blue,
and zero opacity. It's a black transparent pixel. Second pixel is also zero red, zero green, zero blue,
and zero opacity. Zero opacity means these pixels are
completely transparent. The only visible
pixels on canvas are the ones making
up our fish drawing. If you search through the array, especially in the middle, you will start seeing
values 0-255 here, where each value
represents red, green, blue, and alpha of
each individual pixel. I get all zeros here because a big portion of the
canvas is transparent. There is nothing here until we start drawing the
fish down here. The pixels themselves are
of course much smaller. I'm just using this pick red
square as a visual help. Imagine the red square being one times one pixels in size.
28. Extracting coordinates and colors from pixel data: I delete the console. We drew image on Canavas and we
analyzed its pixel data. Now we have color of each
pixel on Canavas store. In this pixels variable, I want pixels variable to
point to the array directly, not to the entire
image data object. I say data pointing towards this unsigned
eight bit clamped array. Now I will cycle through that array row by row,
from top to bottom. If alpha value is
more than zero, it means those are the pixels
that make up the fish, the non transparent ones. When I find a non
transparent pixel, I will create a particle object for it using our particle class. Each time we find a
non transparent pixel, we will create particle
that represents it. To cycle through something row
by row from top to bottom, we can use so called
nested four loops. Nested simply means it's a
loop inside of another loop. The outer four loop will
handle vertical y coordinate. It will jump row
by row from top to bottom until we reach canvas
height and then we stop. I don't actually want each row
to be only one pixel high. I want to jump by bigger
increments which will pixelate our image and it will make the effect
more manageable. We can't really create 40,000 particles and still
get smooth animation. It is possible to do that, but that would require
advanced optimizations that I won't be
covering in this class. We want to decrease
resolution a little bit. Instead of jumping by one
pixel when we finish a row, we will jump by a certain value. I call for example a gap. It's not really a gap,
but more like size of individual particle pixel
that will make up our image. We will be able to
dynamically change it. It doesn't really matter
what value I give it. Now just remember that higher value in Gap means
better performance, but more pixelated image, I said gap to, for example, five pixels. Every time we finish going
through one pixel row, we jump by five pixels down and we start
analyzing those pixels. Every time we enter a new row, we will go from left to right until we reach the
width of canvas area. Each time we jump from
position to position, we jump by the gap value. These nested four
loops might be a bit challenging to wrap your
head around at first. The outer loop is a
vertical y position jumping from row to row. The inner loop maps
horizontal x positions going from left to
right for each row. Let's say we start
here, y is zero. We enter the inner
loop and we go from x05 pixels at a time, step by step, until we reach the width, the end horizontally. We exit the inner loop. We enter the outer loop, and y increases by five, and we are on another row. We enter the inner loop. Again, we go from left to right, five pixels at a time. When we reach the end, again, we will exit the inner loop, enter the outer loop,
y increases by five. We enter the inner loop and we go over horizontal x positions. These loops will
keep going until we reach the bottom of canvas,
canvas height here. At that point, we cycled through all the
pixels in the image. This technique is useful for many other creative
coding effects. When you need to
cycle through a grid, it's a good thing to know and it becomes much easier
when you use it. More often, we are going
through the entire canvas, row by row, five pixel jumps. Each time we jump to the next cell in
this imaginary grid, we want to check if there
are any colors there or if we are currently going
over the transparent area. If we find some colors, we create one particle
using our particle class. We want to make that particle remember its x and y position, its position in the
image and its color. The challenge is that
as we cycle through this massive pixel data
array returned by get image data and stored
in pixels variable, the indexes of the array go from zero to tens of thousands. We need to find a way to take that linear series of numbers and get x and y coordinates
from it, and color as well. Let's start by
getting the index, the current index of the
pixel we are currently going over in our grid in that
massive pixel data array. This formula is a bit advanced, so don't worry if you
don't get it at first. The current index of the pixel
is y from the outer loop, the number of row as we go through the image
row by row from top to bottom times the width
of canvas we are analyzing. That will give us, for
example, a block like this. Then we add the leftover
horizontal pixels, depending how far along we
are in the new row plus X. For example,
something like this. We are five horizontal
pixels into the new row. All of that needs
to be in brackets and multiplied by
four because we know that in the unsigned
eight bit clamped array returned by get image data, each pixel is represented by four positions in that array. Four values for red, green, blue, and alpha. We want that actual array index as we jump from pixel to pixel. This line is here to
make sure we know where in the pixel array at which
index we currently are. As we cycle through the pixels
in the image, row by row, we are mapping a linear line of numbers to X and Y coordinate. If you still don't
fully understand it, feel free to rewatch
this last part, or don't worry
about it too much. Being able to understand logic like this is for math experts, we are creative coders and
Javascript developers. You don't really
need to understand advanced formulas like that, especially if you
are a beginner. You just need to know
when and how to use them. We know that each pixel is represented by four
integers in the array. We know that red pixel value is the first one pixel array from line 39 current index in that array we are
currently on like this. We know that green value for that same pixel is
next index plus one. Then there is an integer representing blue
value index plus two and we have alpha
opacity index plus three. After that, we have again, red, green, blue, and alpha
for the next pixel. Now that I have red, green, blue, and alpha of that pixel, I can concatenate it into a good old standard
RGB color declaration. I can say RGB opening
bracket in quotes like this, plus red from line 43 plus plus green plus coma plus blue
plus a closing bracket. The thing is, we don't
really care about transparent pixels
this entire area. We are not creating
particles for the transparent pixels in this area. We care only about the pixels that make up our
angle fish image. The pixels around
are white because the body of the website
behind it is white. The canvas itself is
transparent in these areas. It's alpha, there is zero. I can say if alpha from
line 46 is more than zero, meaning that pixel in that
area is not transparent. Only then create a particle
for that position. We create a particle by taking
particles array from line 29 and calling built
in array push method. We push one new particle
object created by our custom particle class from line seven inside the array. Our particle class right
now only knows how to randomly spread black square
particles around canvas. We need to teach
it how to expect this new pixel data and correctly arrange itself
to form an image. If you feel that I'm going a bit too fast right
now, let me know. I will recap this pixel analysis
technique step by step. Once we have the entire logic in place, we are almost there. Inside initialized method, we just cycled through
the image row by row, capturing X and Y positions and RGB value of each particle. I need to make sure that
particle class constructor expects these values like this inside I convert them
into class properties. Rather than setting this X to the X value passed
as an argument, I will create a new
property called origin X. This property will
always remember where that particle sits
in the overall image. I set it to X that was
passed as an argument. This X was calculated in
our nested for loops. After we analyzed canvas for pixel data origin Y will be y. That was passed as an argument. I will wrap these values in
mathoed floor to round them down just to make sure we don't
get any sub pixel values. And canvas doesn't have to use antialiasing to
smooth the image out. It's good to mathod
floor everything we draw on canvas for
performance reasons. This color will be color passed
as an argument from here. It will be coming
from that RGB color we concatenated a while ago on line 50 so that each particle retains the original
color of the image. Okay, now particle
class constructor on line eight expects effect x, y, and color as arguments. I go down inside our
main effect class again, whenever I create
one new particle, I pass it this keyword to represent this entire
effect object. As the first argument, I pass it index from the inner four loop as
horizontal position, y from the outer four loop as vertical y position and color. That's how you analyze image for pixel data and convert it
into a particle system. If this was a bit too
complicated for beginners, if you are still
following, well done, now it's time to have
some fun with Animation.
29. Turning images into particle systems: Just to recap,
initialized method will run just once on the
first page load. First we draw our
image on canvas, then we use get image data to analyze the entire
canvas element. For pixel data, that
pixel data comes in a special array where every four elements
represent one pixel. It's red, green, blue,
and alpha values. We put it into nested
four loops like this. To convert a linear
series of numbers, indexes in the array into
x and y coordinates. We use width and height of
the image to know where each row and each column
breaks to map them correctly. As we jump from cell to cell in our imaginary grid of five times five pixels over
the entire canvas, we extract red, green, blue, and alpha values if
alpha is more than zero, if the pixel is not transparent, we create a particle at that position using our
custom particle class. Now let's draw and
animate this in it. Method will run just once
on the first page load. Let's consoloc effect to see if we have all pixel
particles there. I can see my effect object here created by our
custom effect class. From line 28 inside I see particles array we
defined on line 32. It contains 2,775
particle objects. This number will depend on
the image you are using, how much of the image
is transparent, and also on the resolution at which you scanned the image. In our case, we scanned
it at resolution five times five pixels defined
in this gap property. On line 38, I can see that each particle has
color reference to the main effect object, origin X and origin Y positions, horizontal and vertical speed, and the current X
and Y positions. If you see undefined in any
of these in your project, it means there's a typo
somewhere in your code. We have to make sure
all these properties have values for the
effect to work. Uncommen animate on line 76, which will immediately delete the fish image and create
a set of black particles. I want those particles
to start moving to their origin x and origin y positions to
recreate the image. Before we do that, let's
just set this dot x to x. That was passed as an argument. This dot y toy like this. Yes, the fish shape
is there perfect. Each particle also remembers
the color of the image. We just need to use it. Let's set Phil style to
this dot color like this. Nice. The fish is
breaking into pieces because of VX and VY properties. I set V x zero and
VY to zero perfect. Now you know how to recreate an image as a particle system. I have this dot gap
property on line 39, which determines how much
detail we want in the image. The lower the number,
the more performance demanding this effect will be. Gap is five pixels
and I'm drawing particle blocks of
ten times ten pixels. There is a lot of overlap. I can set it to five, or when I do four there
will be one pixel. Gaps in the grade can make it dynamic by saying
this effect Do gap. That way the size of
particles will change automatically as we change
this dot gap property. Large value means
lower resolution. Don't use any
decimal points here. We don't want sub pixel values
for performance reasons. Also, drawing particles as rectangles is more
performance efficient. I made a tutorial for all the version of
this effect before, where each particle
was a circle. This new version with rectangles
will be much faster when we start animating it and
make it interact with mouse. Drawing rectangles is faster than drawing circles on canvas. And when you have 3,000 shapes, 3,000 particles or more, you can really feel
the difference. We can make the gap even
two or three pixels, which will result
in a sharp image. I don't recommend
doing one pixel. That will give you way
too many particles depending on the image size, and it will run slow
when we animate it. There are some canvas
optimization techniques and algorithms we can use to make even one pixel particles
move smoothly. But I won't be covering
that in this course. I can make particle
size always one pixel smaller than the current gap
value to get some space in. You can play with the
values to see what happens. So far it looks like we
just pixelated the image. But keep in mind that each pixel block is
a particle object, can give it physics
and behavior. For some really cool effects, I said gap 23 pixels. For now, I will consoloc
particles array from line 33 by saying effect
particles array like this. Because I said gap to
three pixels we get 7,557 particles that
make up the fish image.
30. Animated particle transitions: I want the particles to always try to move to their
original position. So that when we push
them around with mouse or we break the image
apart in some other way, particles are always being pulled towards their
original positions. I want the image to always automatically recreate itself
whenever it is broken. We will write that logic inside update method on particle
class. Let's start simple. Without any physics,
this position plus equals the difference
between origin position, which is the particles
position in the image, and its current
horizontal position. Y plus equals z dot
originy minus ty. We want particles to always
be aware about the difference between their
current position and their original
position in the image. We are calling update
method on effect class here inside animation
loop on line 74. Inside that method, we are
triggering update method from line 23 on every individual
particle object. Here on line 63, let's put these
values in brackets and let's spread the
particles around canvas. At the initial page load, this x will be method random between zero and canvas
width times dot effect. With to actually
see the movement, I need to make sure that
individual step per frame is just a small fraction of
the difference between the current exposition and
the original exposition. Like this. I do the
same for dist y. I set the initial vertical
position to a random value between zero and the
dist effect dot height, which is the height of the
available canvas area. Let's do very simple physics. I will call it, I set it to 0.2 and I use it here and here. By changing this value, we change how fast or how slow the image
assembles itself. It might be interesting
to randomize this value and have every particle move at a
different speed. Maybe if you want slow motion, make sure that is a
very small number. Well, this is pretty cool. We can have so much fun
with this code base. Now, isn't it great
what you can do with just one Elga script? We are not even
using any libraries. We wrote all this
logic ourselves. We can play with this
effect in many ways. Even just changing
the start in x and y position will give us
something different. Like when I sat starting x to zero or I spread starting y
across a much larger area. We can also do simple
shrinking effect. This would look different if the speed or easing of
each particle is random. There are many ways we can
play with this code base. I can also spread
x and make Y zero. I'm just having fun with it now.
31. Animation on button click: Let's create a button
that we clicked. We'll randomize X
and Y positions of each particle and we will get a nice animation of image reassembling itself every
time the button is clicked. I will call that,
for example, Warp, like a teleport from
sci fi movie when people get broken into
particles and reassembled. When this Warp method runs, horizontal x coordinate
of each particle will be randomized between
zero and effect width. Y will be a random value
between zero and effect height. Like this, I will also give it a different value just
to experiment with it. I go to the main effect class. And the same as we did
with draw and update, I create a warp method. When triggered, it will cycle
through particles array. For each particle
object in the array, it will run warp
method from line 28. Let's go to index
HTML if you want that long base 64 data string that contains our image
to be on one line. In visual studio code
editor you can select view word Wrap from the
top navigation panel. Here we have a diff
with a class of controls and inside
there is a button with an ID of Warp button
back in script GS. I go down here and we will manage that Warp
button functionality. I create a constant variable, I call Warp button
and I set it equal to document get element by ID
Warp button. Like this. Then I take it and I add event listener for
a click event. In the callback function, I take an instance of
effect we defined on line 35 and instantiated on line 76. And I call its associated public warp method from line 71. Like this, this main Warp method will call Warp on each
individual particle, randomizing x and y positions and setting as to
a different value. Now your users can trigger this school animation by
clicking on the Warp button. Awesome, let's make the
button a bit nicer. I'm sure you know how to
style something with CSS, and this is not a CSS course, but let's do it anyway. I take all buttons on the page. Currently we have only one, And I set padding to 20 pixels, font size to 30 pixels, margin to ten pixels. Padding ten pixels
and margin 20. Maybe I open another
browser window and I will go to
Google Fonts website. I will search for a font called Bangers for the school
comic book art style. I click this plus button
to add it to my library. Then I click View
Selected Libraries, and it gives me these
three link tags. I can just copy them and paste them up here in index HTML, I make sure I put them before my custom style CSS file to make sure it's
available there. Then all I have to do is
to copy this CSS rule for font family and I
apply it to controls. No, okay. I have to apply it
to the button tag itself. Let's delete this.
Perfect 40 pixels, font size, text color white,
background color black. Just to make it interesting, on hover, I swap the color. Text color, black,
background white. Nice. Let's give a
transition of 0.2 seconds. Okay, I like this.
We turned image into particles and we added a warp pattern that
will play a transition. It will show off how our image automatically reassembles
itself when broken apart. Let's add more physics
and interactivity by making individual particles
react to the mouse.
32. Mouse interactions and particle physics: I want the main effect
class to always be aware of the current position
of the mouse cursor, I create a custom
is mouse property. It will be an object with
three properties radius. I will initially set to
3,000 It will define the area around the cursor where particles
react to the mouse. It won't really be 3,000 pixels. I will explain why the number is so high when we get there, it's for performance reasons. X and y position of
the mouse cursor will be undefined at first. Class constructor runs
once at the point when the class is instantiated
by calling the new keyword. We can take advantage
of that and we can actually add any
code in here that we want to be executed at the point when effect
class is created. I can even add event
listener in here like this. We will listen for
mousemove event. I need to make sure that
this event listener is bound to its
current lexical scope. I want it to be inside
effect class constructor so that I can override mousex and mousey properties
as mousemove event fires. To make sure that the callback function on this event listener remembers where inside which
object it was first defined, I can use a special
Java script bind method or change the callback function into an ES six arrow function. Sometimes we also call
it fat arrow function. One of the special features of arrow functions is that they automatically bind this keyword to the surrounding
codes context. It will always be able to
see this domouse property, which is exactly
what we need here. We need access to the
autogenerated event object, since this arrow function
will have only one argument. I can even remove the
brackets like this. Whenever mousemove event fires, I take this dot mouse
dot x from line 47 and I set it to event x current horizontal coordinate
of the mouse cursor. I will also set this dome
doty from line 49 to event y. Like this. Let's test if it works by consologin
mousex and mousey. Because I put that
mousemove event listener inside class constructor
of effect class. It will get automatically
applied when we instantiate effect
class here on line 86. Save my changes, refresh
my browser window. Now when I move
mouse over canvas, I can see mouse position is updated and updated values are being correctly saved in our custom dot mouse
property on effect glass. Perfect. Let's delete
the consolog on line 88. Also, this one on line 54. It's good to always delete
consologs like this. For performance reasons, we are capturing the current x
and y coordinates of mouse. Here we want each
particle to keep checking how far it
is from the mouse. If the mouse is close enough, we want the particle to
be pushed away by it. We will write that logic
inside update method on particle class x. Distance on the horizontal
x axis between mouse and this particle is
dot effect mousettx from line 50 here minus st x dy Distance between
mouse and particle on vertical y axis is this
dot effect dom y minus disty. To calculate a distance
between two points, we can use Pitagorian
theorem. It works like this. This is mouse, this
is a particle. We create an imaginary
right triangle between these two points. This is the x
horizontal distance. We already calculated
that on line 25. This site is a DY distance
on the vertical y axis. We calculated that on line 26. To get this site, which is the actual distance
between these two points, we use Ptagora's theorem
to calculate hypotenus, the longest side of a triangle opposite
to the right angle. Pitagora's theorem
formula is C is equal to square root a squared plus b squared converted
into Java script. We say to distance is
mouth square root from disto dx times stod x which is a squared plus so
times Didi squared. That gives us the distance
between two points, between particle and mouse. I remove square root
because this is a very performance
expensive operation and we don't really need it. I just need to adjust
mouse radius to a larger value if you are
not using square root here. That's why I set mouse radius
to 3,000 here on line 51. You can adjust the
range when you see how large it is when the animation
is going in a minute. I want to implement
some physics. I want the particles
to be pushed harder if they are
closer to mouse. I do that by creating a help
er property called to force. It will be a radius
of the mouse, the area around the mouse
where particles react to it, divided by the current
distance between the mouse and the particle to make the particle moved away from the mouse in the
right direction. I need to put minus here. We will see how it works
when we have some animation. You can try to put plus and minus here and see what happens. The force at which the
particles are being pushed is equal to
the ratio between the radius of interactive
mouse area and the current distance of that particle from
the mouse courser. I check if distance is
less than mouse radius. If it is, we push the
particle away from the mouse. Before we do that, I'm using some new class properties that aren't defined in
the constructor. Let's define them first. Dx, dy dot distance,
and didot force. All these properties
will be zero at first. I will also create
another property I call for example, dot angle. Dot angle will determine the direction at which the
particles are being pushed away when they interact with Mouse down here
we will calculate that angle using matote a 102
built in Javascript method. Matote 8102 method
returns a numeric value in radiance between
minus pi and plus pi. We know that circle is
two pi in radiance. This value that matoteen
two returns is representing so called theta angle between a given x and y point and
positive x axis like this. Important thing to remember is that this built in
Javascript method expects a value of
the y coordinate first and the x
coordinate second. The difference between
x Andy position of the particle and the x Andy
position of the mouse cursor represented by
Diddy and dis dot x past the method 102 method
gives us an angle value. We will increase
horizontal speed, Vx velocity x, by
force from line 33. That depends on
the ratio between mouse radius and the
distance between mouse and particle making particles that
are closer being pushed by a bigger force to specify which direction
they are being pushed. I pass this angle. We are calculating on line
3062, most cosine method. This method will map that
angle we pass to it in radians as a value between
minus one and plus one. This value will represent the cosine of the
angle and it will make the particles float
along the outer edges of the circle nicely as the
mouse interacts with them. These a tin two and cosine methods and
everything to do with trigonometry is quite difficult to explain if you see
it for the first time. I don't think it's important to fully understand
it as long as you know how to use it and how to
get the movement you want. When we get the animation going, I'll play these values
to see what it does. The visual feedback we get when the animation is going might make it easier
to understand. This is hard, so
don't feel bad if you didn't follow all this
trigonometrical logic. It doesn't affect
your ability to be a good Javascript developer. Now we updated VX
from line 16 by accounting for force of the push and direction
of that push. We will do the same thing
for vertical speed, but this time we will
use method sine. We pass at the same angle. Method cosine and
method sine work together to map a position
along the radius of a circle. Now I can use VX and VY
vertical and horizontal speed, x and y position
calculated here like this. Let's try first to see what it does while particles
certainly react to mouse. But something is wrong. I'll fix that in a second. To improve the physics, we
can also apply friction. I create a property
called friction. I set it to 0.95 I
multiply V x by it here, making it slowly decrease
in its strength. For every animation frame, the motion is wrong. But still, I think it's interesting to
check what is happening. I can see I made a
typo here on line 33. It's supposed to be D x
times dx, dy times dy. Like this, Pitagra's theorem. Now, the particles behave
as expected, perfect. We are pushing them around to make them return to
the original position. We have to do times equals here. If I change as to 0.2 we
get much faster motion. The lower the value of friction, the more friction
will be applied. There is a lot of math here. Don't focus on that too much. This course is mainly about Java Script and
drawing on Canvas. You can use these math code
snippets for other projects. Even if you don't
fully understand how the math works yet, I encourage you to
play with the values change minus swap signing sign, try to break it and see how
it affects the movement. I hope it will make
it more clear to see what is happening at what happens when I increase
friction and increase radius, we get this more
gradual boundary. There is an extended version of this course linked in
the description below, where we take this even further and create some
special transitions. You can check it
out if you want. I hope you found
some value today. I'm here for you
to help you learn Java script and discover all the amazing things
you can do with it. Do you think this was
a difficult project with all the algorithms? How difficult was it for you? Give me a number
in the comments. One is extremely easy, ten is very difficult, five is something in
between, I feel later.
33. Creating unique animated transitions: Let's swap the image
to something else. You can use your own PNG file with a transparent background or you can download the same image I'm using in the Assets section. This image is another character from my other Javascript
course where we explore an alien planet and it's strange
mechanical life forms. We can convert image into base 64 String with Java Script. But it's quicker
to use an external website if you want to see how I convert images drawn on Canvas to a data like
this on the fly, dynamically part of
Javascript code base. Check out my other
creative coding course about lines, rotation,
and fractals. In there, I explain step by step how to create my
procedural fractal. In effect, same
as we did before. I Google PNG to base 64. There are many sites
that can do this. Just make sure that
the converted code starts with the
data colon image. If it doesn't take
this checkbox, I copy the long line of code. I go to index HTML and I want to replace it
here on line 15. It's easier if I enable word
wrap In visual studio code, I click View Word Rap. I want to replace all this
code in source attribute. This code is the entire image. In visual studio code, I can just highlight the
portion in the beginning. Then I scroll down
where the code ends and to highlight
everything in between, I press and hold
Shift key and I left click where I want the
selection to end like this. I deleted and I paste in the new code we copied
from the website. Here we go, we swap the image and everything works awesome. This effect is really
fun to play with. I find it very relaxing. For some reason, I adjust
friction on line 19, I said mouse radius
to 3,000 For example, back in index HTML, I go to view word wrap
to toggle it off, it's better to
have the long base 64 data string on a single line, otherwise we have to
scroll down a lot. I copy this markup for work button and I give it an
idea of block button with text that says blocks in script GS. I copy this code
block and I adjust it to point towards
that new block button. In visual studio
code you can multi select like this by
highlighting a text each time you hold
down command on Mac and control on PC and
you press letter D, you will select another
occurrence of that text. I save a reference
to block pattern, I attach event
listener to it and inside I call effect
block public method. We need to create
it. I make sure I'm inside effect class down
here where we created Warp. I create blocks method. Its job will simply be to cycle through
particles array from line 56 For every particle
object in that array, it will trigger method that
is also called Blocks. We need to make sure that
each individual particle has a method with that name. I go up here to particle class and here where we
created Warp Method. Before I create this
new blocks method, inside I can do all different
kinds of things to mess the particles up
because we know they will assemble back
into an image shape. Again, because of the logic inside update method
we wrote before, I can, for example, split
particles into two groups. One group will be coming from the top of canvas,
coordinate zero. The other half of particles will be coming from the bottom of canvas from
vertical coordinate equal to canvas height. I can do it by using so
called ternary operator. It's the only Java Script
operator with three operas. This case we will use it as a simple one line
if L statement. The structure is simple
condition to evaluate, question mark,
something to do if the check evaluates
the true colon. Something else to do if
it evaluates the false. The condition to
evaluate will be to check if method random is more than 0.5 Method random called
on its own, like this, will return a random value 0-1 It should be higher than 0.5 in roughly 50% of cases for those particles set their
vertical position to zero. The top for the other
half of particles, set their vertical Y
position to affect height. The bottom of canvas
ternary operator condition to check if true, do this, else do this. Now when I click Blocks pattern, it splits the particles
into two groups, coming from top and bottom. Nice, I can change the value in actually in Warp
will be 0.2 in blocks. We can try different as that way when we click
different buttons, particles have different
easing different speed, we can change any properties
of the particles we want. Here I can, for example,
make them larger. Let's set size of individual particle rectangle
to ten pixels. Interesting. The problem
is when I click Warp, now the size of particles
stays at ten pixels. I have to remember when I manipulate a new value
in blocks method, I have to define it for
Warp method as well, if I don't want the size
to be the same Like this. Now Warp has small particles, blocks gives us
large pixel blocks coming from top and bottom.
34. Particle assemble effect: This next effect is really cool. In index HTML, I create a new button with an
ID of assemble button. And text says assemble
in script GS. Down here again, I create
a section where I create a variable to store a Java Script reference
to that new button, and I give it an event
listener inside. We call effect assemble. Inside Effect class,
I will create a top level version of assemble method that will
manage all particles, same as we did with Warp and Blocks up here inside
particle class, I need to define this
new assembly method and give it some logic. Inside we set x to a random number between
zero and effect width. Y will be a random number
between zero and effect height. I want the particles
just to be randomly spread around the
available canvas area. I need to define ease and size. Since we are changing
these values on under buttons as well, it does the same thing as warp. I want it to behave differently when it's reassembling
the image. I want the particles to go back to their original
position in the image, but not all at once,
but one by one. At a specific speed, we will have to create a
switch that will enable and disable each particle's
target seeking behavior. Right now, the particles
always want to return to their home
position in the image. I need to have a switch that will prevent them
from doing that. And create a traffic light
system that says stop and go. I will write that
logic that will delay particles from getting back
to their original position. And then I will recap
the entire logic loop. Don't worry too much
if you don't fully understand everything
in the first round. Let's write it first. Down
here on the main effect class, I create a property
called this counter. Initially, I set it to zero. This countervariable will
be slowly increasing. As it increases, it will allow more and more particles to move back to their original
position in the image. Each particle will wait
a certain number of milliseconds before it starts moving back to
assemble the image. That delay in milliseconds
will be equal to this countervariable inside
the master assemble method, I set counter to zero. This is important
because we will have another transition that
uses the same counter. We need to make
sure that counter is always zero when assemble button is clicked or as we click between different
transition buttons. We will have here, it will
make more sense in a minute. Each time assemble method is triggered on each
individual particle, they will be
cumulatively increasing that same single
counter variable by calling assemble on
each particle object inside particles array, each particle will push
counter value higher by one. I will create a
set timeout here. Java Script set timeout
method calls a function after a certain number of melliseconds set timeout sits on
the window object. Same as request animation frame. We can call it
directly like this. It expects two arguments
function to call and delay how long to wait
until we call it as a delay, I will use this dot effect
to counter the value that increases by one every time each particles assemble
method is called. That way, each particle will slightly delay the next
one coming after it. As for each method inside
the main effect class calls assemble method on each
particle object one by one. The function to call after
that increase in delay will be an arrow function because we are inside a class,
inside an object. And I need to make sure
that this keyword is bound to the context where
the set timeout is defined. As we said before, ES
six arrow function binds skewed to its current
scope automatically. That way I can use this dot
inside here and it will work. Alternatively, we
would have to use the Java Script
bind method here. Using arrow function is simpler syntax and it does
the same thing as I said, I want to create a
traffic light system where particles are
told to stop and go. We will do that by creating one more helper property
called this dot active. When particle has active
set to true, it can move. If active is false, particle will freeze at
its current position. After set timeout,
timer goes off. We set active to true up here in particle
class constructor, we initially set
active also to true. Inside update method, we
have code that reacts the mouse code that returns particle to its original
position to assemble the image. I want all this code to run only if active flag is set to true. Down here when assemble
button is pressed, we randomize x and y
positions of each particle, spreading them all
around canvas. We set easing speed and size. At that point, as the
particles are spread out, we set this active to falls, freezing them all in place. As the for each method runs
inside the main effect class, it cycles very quickly through the entire
particles array, from the starting index
to the ending index. Each time this code
runs for new particle, the counter slightly increases, increasing this delay value. Each particle in
the array will have slightly higher counter value, causing this set timeout to be called with an
increase in delay. When set timeout
finally triggers, each particle will be
set to active true. And the if statement we created inside update
method on line 32 will finally allow it to move back to its
original position. While at the same time it will also allow mouse interactions. That was fun because the value of timer is equal
to the delay in milliseconds that each particle has to wait before
it becomes active. Increase in timer by a larger value here
will actually make the assembling slower particles have to wait more milliseconds
before they can move. The speed of the assembling
itself will also depend on the image and what resolution you have in this gap property. Because if your
image is made out of less particles that
are bigger in size, it will assemble faster. You can also slow it down by multiplying the counter
value by something. That way you are also
increasing the milliseconds. Therefore, you are slowing
down the assembling process, Multiplying it by anything less than one will speed it up. I think this is really cool how particles wait in place
until it's there, turned and then they jump into position to
recreate the image. We can also play with
mouse interactions as the image is assembling
and it will still work, they will just fall back in
place when we allow them to. This is a pretty solid
code base, I think. Feel free to do your own
experiments with it. One problem we have here is that when we click
Assemble button, it will instantly set that timeout sequence
for all the particles. And if we swap out and back into this
assemble transition, if the timeout hasn't
activated yet, the animation will be starting from somewhere in the middle. We also need to clear all
timeouts, all particles. Every time we activate
assemble effect that the image always starts assembling
from the beginning. One way to clear timeouts is to capture them
in a variable. I create a new variable
called this dot timeout. Initially, I set it to
undefined down here on line 68, where we create a new timeout. We set it equal to
this dot timeout. It will actually activate
as it did before. Setting it equal to a
variable like this will have no effect on how the
timeout triggers itself. It, however, gives
us an easy way to clear it when
assemble is called. First, I want to clear
the timeout if there is an active one clear
timeout method in Java, cancels a timeout that was
previously established. By calling set timeout, it expects one
argument which will be an identifier of the
timeout we want to cancel. Set timeout returns
an ID when called, but we have it in a variable, so I can just pass it
this time out like this. And it will work in here. I'm actually not sure why it's still going
from the middle. It has something to
do with this number being too small because
when I do this, it will reset correctly. It works with larger numbers. When I go into too
many decimal points, it assembles from the middle. I think it is, because
the number we are passing as milliseconds
delay is too small for the beginning indexes in the array like this,
it works really well. Maybe it's better to adjust assembly speed by
controlling by how much the counter increases
here on line 68 experiments.
35. Particle print effect: Let's create one more
transition effect. I call it particle
print because it reminds me of three D printer. Creating something layer
by layer, row by row. I create another
button with an ID of particle print button and text
that says particle print. As usual, we bring the
button into the project. Down here in this
area of script, we give it an event listener for click event and we will
call particle print method. We create particle
print method down here, same as assemble. It will also need counter
to be reset back to zero. Then it will cycle over the entire particles array
from start to finish. It will trigger
particle print method, which we will define. Now I define it up here and it will work very
similarly to assemble method. This time I want the
initial x coordinate to be exactly in the
middle of canvas area. This effect with times 0.5 Now all the particles come from this single
line in the middle. If I said the
initial y coordinate to the middle of
canvas vertically, all the particles will be
squashed into a single point. We created a pixel singularity. From there, the image
will assemble as canava, reverse black hole, spitting out particles and
decreasing entropy. I think this looks really cool. Which transition is your
favorite Warp blocks assemble or particle print. Leave a comment to let me know. I hope you found some value
and learned something new. Check out my other
classes if you want to explore more
creative coding or two D game development with vanilla Java sq, see later.