Liquid Particle Effect Around any Element, Vanilla JavaScript & HTML Canvas & SVG Animation Tutorial

Liquid Particle Effect Around any Element, Vanilla JavaScript & HTML Canvas & SVG Animation Tutorial


Today we will build interactive particle
waterfall, that reacts the mouse and can flow around any HTML element. Hi coders
welcome to another episode of my vanilla JavaScript and HTML5 canvas animation
series, where we create all different kinds of interactive animated effects,
from scratch with no libraries, using just pure vanilla JavaScript. When I created
this effect I was thinking about using it for something like personal coding
portfolio or maybe some flashy advertising website. We do crazy coding
experiments here, we don’t do subtle 🙂 You can get creative and make it float
around other HTML elements to highlight them or it doesn’t have to have any
purpose at all just go along with me practice and learn new object-oriented
programming techniques. We are using vanilla JavaScript and particles so we
will work and talk about prototype based inheritance, simple collision detection
algorithm that can also be used in games and some array manipulation. First we’ll
build simple layout with HTML and CSS although that’s not the main purpose of
this video. our main focus will be the interactive
digital particle waterfall. I love this sticky goo effect I’ve used it for so
many tutorials here on the channel already. Don’t forget to get your coffee
and let’s start! I built alternative versions of this effect combining it
with my vanilla JavaScript particles algorithm if you want to see tutorial on
that let me know. First we will build a CSS layout if you want to skip straight
to the JavaScript part there is a timestamp in the video description. I
asked people on Reddit what should we call this effect. That was interesting. As
usual we will work with three files index.html style.css and script.js. In
index.html We link style.css file and script.js and create HTML markup. Canvas
with an ID of canvas1, div with a class of container, inside div with a
class of content, inside HTML semantic tag
nav which will help disabled people who come to our site using screen readers.
Our navigation will have five button elements, next to the nav element we
create a sibling tag section with a class of main block. This will contain
diff with placeholder text for now inside main block section there
we’ll be two other sections topBlock and bottomBlock. Try to use semantic
tags like header, nav, section, button article, aside or footer as much as
possible. These tags have a lot of built-in functionality and help disabled
users to navigate our site with ease. It’s considered a good practice to
always use semantic tags over divs if possible. I’ll just quickly put in some
placeholder text these are Futurama quotes in case you’re
wondering. I love that show. In style.css we use asterisk * selector to target all
elements and set the box-sizing to border-box. This means any border and padding
will be included in elements a total width and height. I set container to
position:absolute, content will have margin top 100 pixels, nav width 200
pixels, margin left 142 pixels, so there’s enough room for our interactive particle
waterfall. Z-index 50 will make sure this element is always on top and can be
interacted with. Each button will have 100% width of its parent, in this case
200 pixels. 50 pixel height, border 1 pixel of solid black, just to temporarily
show you where the buttons are. Transition 0.3 seconds, position:relative
font color white, margin bottom 5 pixels to space the buttons out a bit and font
size 14 pixels. Button tag comes with some preset styles. I’ll reset border,
outline and background. For each button we create a before pseudo-element I will
explain why in a second. Mandatory content attribute is empty string, width
100%, height 50 pixels, line height:50px will make sure our our text is
aligned vertically as long as we only have one line of text for each button. I
will position each before element absolutely towards its relatively
position parent which is the button element background black top zero left
zero now I use content attribute to display text on each button I target
each before element one one with :nth() selector and set its
content value to home project contact about an hire me. I was thinking about
portfolio website when building this effect on hover
we will animate before element to the left by using transform translate X
minus fifty pixels okay transition property needs to be on
before element not on the button the reason we created and why we are
animating before element instead of the button is to make sure the button
doesn’t get janky and doesn’t slide back when mouse hovers over the small white
square area to the right of the button here this is a good technique to
remember when you want to create a good quality working vertical menus like this
I will just build a basic layout so this will be the desktop version I will focus
mostly on the animated waterfall I can make this into a mobile version in
another video if you want but there are many good YouTube channels that will
teach you how to build modern layouts for example Design Course – follow that guy
he’s awesome! Main block element will be width of 100 percent min width 500
pixels to make sure our desktop layout doesn’t break height 270 pixels border 3
pixels solid black position:absolute padding: 38 px top bottom 60 px left
right background will be black text white font size 14 pixels let’s align this now nav will have
position:absolute which will remove it from the document flow and main block
will have margin left around 360 pixels let’s see 350 pixels top block is a
child of main block width will be 80% border to pixels solid
black text color black height 70 pixels and top – 70 pixels to bring it outside
of its parent with position:absolute and left sixty pixels topBlock contains div with text width
will be 80% and let’s just center it using absolute position centering trick
position absolute top 50% left 50% transform translate – 50% – 50%
font size 16 pixels and text-align:center bottom block border two pixels solid
black text color black bottom – 140 pixels and height 140
pixels Don’t know why I typed color black again need
another coffee left 70 pixels with 80% position:absolute the div with text it contains will have
a width of 80% and will be centered both horizontally and vertically main block contains two paragraph
elements I just give the second one slightly different style I use :nth() child
selector text-align:right margin top forty pixels
it’s not responsive at all let’s see what I did here okay container needs width attribute 100%
I will use CSS calc method and make it like 100% – 200 pixels to compensate for
the margin on the left from our navbar 400 pixels seems okay now it’s
responsive for desktop as I said we will focus on waterfall effect I can make
styles for mobile in the next video if you want as you can see buttons don’t slide back
when you hover on the white box next to them this is the best implementation you
can do for this kind of button layout I can hide the border now as you’ve seen
how it works behind the scenes. I think there’s too much white space on top left
let’s change margin top to 50 pixels there is also a canvas element we haven’t
touched yet we just need to make sure it covers the entire page in the background
I give it width 100% height hundred-percent background color black
position:absolute will remove it from document flow actually let’s make it
blue so we can see where it is to remove any potential margins I set top zero
left zero okay margin top back to 150 pixels and I can remove background color
now that we know that canvas is covering the entire background that’s it for the
basic layout I will leave the rest to your imagination let’s now focus on the
waterfall effect in script.js we start with basic canvas setup i target canvas
with get element by ID and save it in a constant variable I call canvas CTX shortcut for context will be set to
canvas dot get context 2d to give us access to built in javascript canvas
object and its drawing methods such as fill style arc begin path and so on
I set canvas width to window.innerWidth and canvas height to window.innerHeight
this will run just once on the first page load it won’t resize canvas when we
resize browser window we will deal with that later first thing we need is to use
JavaScript to monitor Mouse position there are many ways to do this what I
like to do is to create object called mouse with X&Y properties at first let’s
set them to undefined then I create an event listener on window object and
every time a mousemove event is triggered in browser window callback
function will run this function by default can access built-in event object
I will call it just E when I pass it as an argument
every time we move Mouse we set mouse X to e.client X and mouse y to
e.client Y now we have a mouse object the dynamically stores current mouse
position this is very useful for so many JavaScript effects try to get familiar
with this code if I console.log mouse X and mouse Y you can see the values are
being logged the way I decided to build waterfall and have it interact and
collide with buttons is that I will build copies of these buttons on canvas
just to get the collision coordinates but at first I will need to draw these
buttons on canvas as well just so you can visually see what’s going on
maybe there is a better way of doing this please leave a comment if you can
come up with a better solution I tried to dynamically monitor position of
moving buttons in DOM but that seems to be more demanding on performance if you
want the liquid to flow around a static element then that would be viable
choice I create a JavaScript class called button classes were introduced in
es6 version of JavaScript and are different simplified way to write Java
scripts existing prototype based inheritance
you will hear phrase syntactical sugar which means classes
are just a new way to write old code we use classes when we want to create
multiple similar objects in this case I want to create five objects one for each
button these objects will hold coordinates sizes and utility methods
for each of my canvas buttons each class has a mandatory method called
constructor it is a special function for initializing objects created within a
class constructor will expect five attributes whenever we call it to create
a new instance of a button x and y-coordinates width and height and base
X which will just store initial position on the x-axis as we will need it when we
slide buttons to the left around the x-axis each button will also have a
method that calculates its movement for each frame of animation
I call it update but it’s a custom method so you can call it whatever you
want I create a variable called the direction X and set it to 2.2 this will
just determine how many pixels per frame the buttons will be sliding let’s start
with if statement if we detect collision detection between mouse and button and
at the same time buttons current position isn’t not more than 50 pixels
to the left of the base we enter first block of the code which will slide
button to the left else if mouse and button are not
colliding and button is not at its base x position button will slide to the
right the first if statement will be algorithm for collision detection
between two squares in this case Mouse and button if Mouse dot X is smaller
than this dot X plus this dot width this keyword stands for current instance of a
button at the same time Mouse dot X is bigger than this dot X at the same time
mouse dot Y is smaller than this button Y plus this button height and at the same
time Mouse dot y is bigger than this dot Y all of this in parentheses will check
if mouse and button are colliding then another double ampersand
check how far from the base position the button has already slid we did set CSS
transform to minus 50 pixels on the x-axis so we will mimic that Here I am
not too sure about this second part let’s test it and see this.x minus equals Direction X so far we are getting an error so let’s
complete the second part of if else statement else if this dot X is smaller
than this base X this dot X plus equals Direction X so if
there is no collision detection and button is out of its original position
slide it back 2.2 px per frame as defined in our direction X
variable I create custom draw method that will
draw our buttons on canvas for now just so you can see the collision areas I
make the buttons blue beginpath fill rect will draw a rectangle with
values held in each button object and I call closed path canvas built-in method
I will create empty array called buttons to store our button objects now function
I call create buttons for loop will run five times to create five buttons inside
I create a set of temporary variables that reflect arguments expected by our
button constructor method these will hold numbers we will need
for our button attributes top margin hundred to create a gap from the top bottom margin five to have five pixels gap between each button vertically X is 150
which means button will start drawing 150 pixels from the left y coordinate
that will determine vertical position for each button will be calculation of
top margin of 100 pixels plus 50 which is button height plus button margin of 5
pixels times “i” which will increase for each button so one two three four five this
will align all five buttons one under the other height will be 50 pixels width
200 pixels and now I can call array push method on our buttons array and I will
use button class and call it using the new keyword which will create individual
instance of button object and as arguments I pass it values of X Y width
and height we just calculated for loop will repeat this five times creating
five different button objects in buttons array so far I just declared the
function so I call it here create buttons parentheses a now I can console.log
buttons array you see it contains all five button objects we will access these
values and draw rectangles now custom function I call draw buttons for
loop will cycle through the entire buttons array and for each button object
in the array it will run update and draw cuss the method we created on our button
class I feel I say button too much before we write code to handle waterfall
particles let’s just animate the buttons I create custom function I call animate
this function will run over and over for each frame it will clear canvas calling
clearRect canvas method now for loop actually no here I can just
run draw buttons function which we’ll call update and draw method for each
button in our buttons array for each frame of animation it’s not animating and that is because I
forgot to make our animate function to run in loop I call requestanimationframe
method and pass it animate the name of its parent function that way we create a
recursive loop and this code will just run over and over animating our canvas
yes it works now the buttons are not aligned let’s increase top margin here
and button margin here it’s not ideal yet we will align it better in a minute
you can see the button slides back when I hover over a small white square space
to the right of it to deal with it I can just make the button go wider instead of
making it slide I do it by changing this dot width value in our collision
detection if statement like this let me just hide CSS buttons for a minute to
see what’s happening on the canvas still not perfect getting there when I resize the window I can see we
are still not aligning the buttons properly I change margin to five when I resize the window canvas
stretches as you can see to fix that I will add to event listener on window
object and every time resize event occurs I will set canvas width to window
innerWidth and canvas height to window.innerHeight fixed let’s focus on particle waterfall now
same as we did with buttons I will create a JavaScript class and call it
particle we will use this to create array of particle objects particle will have a
constructor that expects arguments for X Y size and weight the syntax might look
weird but it’s very simple constructor will run just once for each particle
when we call particle class using the new keyword we will pass it arguments
for X Y size and weight and all the constructor is doing is assigning values
from arguments to that particular instance of particle object so this
objects X attribute is equal to X that was passed in arguments and so. Update
method will calculate position for each particle before we draw it for now
let’s just do if statement that checks if the particle has fallen down over the
bottom edge of the canvas if this dot Y is more than canvas height set this dot
Y to 0 minus this dot size that way we will just transport the particle behind
the top edge of canvas and it can fall down again over and over
we will also randomize particles position on the x-axis I want it to fall
from anywhere between 200 and 260 px form the left edge of the screen if the particle hasn’t fallen of the
screen the entire if statement will be ignored
and it will just animate particles along the y-axis the speed of animation
depending on particles weight value now we can draw our particles at its newly
calculated position with a custom draw method fill style will be nice light
water blue begin path canvas arc method to draw a circle with current particles
values and CTX fill to fill it with color again I create array to hold our
objects particle array variable set to an empty array number of particles let’s
say 80 function create particles with for loop that will run 80 times we
create a set of temporary variables and pass them to particles constructor as
attributes using “new” keyword to individually create particles with
randomized values one by one position on the x-axis will be a random number
between 200 and two hundred and sixty pixels from the left edge of the screen
position on the y-axis will be a random number between zero and canvas height weight we’ll be a random number between
0.1 and 0.3 and size will be random between 20 and 25 then we call push
method on our particle array and use a new keyword we create instance of
particles class passing it our randomized variables as arguments now I can add another full loop to our
animate function the for loop will cycle through the entire particle array for
every frame of animation it will call update to calculate particles current
position and it will call draw to draw it not working what did I do oh there’s
a typo in height let’s make the particles heavier but not
only initially but also when they fall of the screen and reset yes we’re almost there you don’t really
need to draw the buttons we just need their coordinates to calculate
collisions this effect is actually kind of nice on
its own to create collision detection between
particles and buttons we go to the custom update method on particle class
and create a for loop this will cycle through all buttons and for each it will
compare button coordinates with current particles coordinates it will be the
same algorithm we used for collision between Mouse and buttons if this
particles X is less than button X plus button width and at the same time
double ampersand this dot X is more than button X and this particles Y is smaller
than each buttons Y plus each buttons height and this particles y is more than each
buttons y we have a collision collision will stop particles from
falling so weight equals zero else if the particle is not colliding
with any of our buttons this dot weight plus equals zero point zero point three
also if they collide slide particles to the left there we go yes I also want to
be able to push two particles to the right if I intercept the flow with Mouse
I will introduce one more check in particles update method to see if it’s
colliding with mouse on the constructor I introduce new property called flowingRIght and I set it to false initially again a familiar algorithm for collision
between two squares this time between mouse and particle if this particles X
is more than Mouse X minus 50 to give Mouse 50 pixels area around it and at
the same time if this dot X is less than mouse dot X plus 50 and this dot X is
more than mouse dot X minus 5 and this.y is less than Mouse dot y plus 5 we will move this dot y and this dot X
slightly which is kind of useless the way I implemented here but mainly we
will set this dot flow right to true now if particle collides with the button
we check if it’s set to flow right or left
so if exclamation mark this.flowRight which means if this dot flow right
is set to false this dot X minus equals four so particle will flow to the left
if flow right is false else which means if this.flowRight is true this.x
plus equals four so particles will flow to the right over the button
but I don’t want them to slide to the right permanently every time particles
fall off screen I will set flow right back to false there we go it works you can keep it like this or if you want
particles to appear like liquid we can apply SVG filter to the canvas
I like this filter and I’ve used it for so many projects already I go back to
index.html and I write the mark-up ID will be goo Gaussian blur first then color matrix filter to adjust
contrast you can tweak values here you see 19 and minus 9 if you want to change
blur breakpoints I will not be explaining this in detail but I will
leave a link in the video description if you want to know more about inline SVG
filters from somebody who really understands it I also add composite
filter now I can apply the filter to canvas
element in style.css file I gave it ID of goo
so filter URL # goo simple I had a small typo here where I
misspelled source graphic it works now yay
fully interactive there are many ways to modify and customize this effect try to
get creative you can add more particles here make the particles different size
or make them different color or make them change color Ink Gold this kinda looks like honey I’ll
leave the color choice up to you if you are struggling with a code you
can direct message me on Twitter my twitter handle should be on the screen
right now thank you very much for watching I will decide what to do for
the next video based on the comments down below See you next time! 🙂