HTML Canvas DEEP DIVE for Beginners

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] creating beautiful interactive animations for the web can be easy html canvas allows a dynamic scriptable rendering of 2d shapes and bitmap images and we can also make games using those same techniques we can draw shapes with code and today we will use vanilla javascript to draw a single line and then we take it to the next level there are a lot of things you can do with lines for example create a fully interactive self animating vector field that reacts to user input in multiple different ways the main techniques we will focus on today will be how to make canvas projects fully responsive with window resize event and cancel animation frame method how to improve code structure we will focus on encapsulation and new javascript edition such as class fields and private class features we will make sure periodic events in our animation have the correct timing with delta time and as usual there will be some crazy experiments at the end and i will share many other small front and web development tips and tricks along the way as i always do are you ready to improve your creative coding skills and become a better front and web developer in the process then join me for this exercise in vanilla javascript canvas animations and object oriented programming i hope you get a lot of value [Music] today my friend radu helped me with some code optimizations for today's project he is an expert on algorithms check out his channel in the video description i will link some interesting project tutorials he prepared for us i create a basic web page markup i link my style css file i create html5 canvas element with an id of canvas 1. that way i will be able to target it with css and javascript and the last thing here i need to link my script.js file which will manage all logic and functionality in style css i do the usual global reset on merging and padding to overwrite any differences between cross browser default settings and i set box size into borderbox which will set elements width and height to include content padding and border it does not include margin all we do with css today is to position canvas i give it position absolute background block top 0 left 0. everything else will be done with javascript in script.js file before any code runs i want to make sure everything on my web page has been fully loaded and is available for javascript to find in this case especially the canvas element one way to do it is to call unload property on global window object sometimes you can also see people call onload on document object which represents the web page the difference is that with document unload the event fires when dom is ready which could be before images and other external content is loaded if you call onload on window object javascript will wait for the entire page to load including its content such as images css scripts and so on after my page has been fully loaded i point javascript towards my canvas element i need to capture it in a variable so that i can draw on it i use document.getelementbyid here now i need to create an instance of built-in canvas 2d drawing api which holds all canvas settings such as fill color and line width as well as all drawing functions for example arc method to draw a circle fill rectangle method to draw a rectangle and so on i will call that custom variable for example ctx shortcut for context and i create my instance by taking canvas variable from line 1 and calling built in get context method keep in mind that getcontext can only be called on a variable that holds a reference to html canvas element it's a special method i can pass it one of two possible arguments webgl or 2d today we will be using built-in 2d api if you console.log ctx you will see all the properties and methods on there if you console canvas variable it has width and height properties i want to make my canvas cover full browser window so i set its width to window dot inner width and height to window inner height like this today i want to show you how to avoid writing so called spaghetti code and how to properly wrap your animations in objects we will follow encapsulation principles as if we are building our own animation library we will of course not use any libraries all the codes today will be plain vanilla javascript and i will explain everything to make sure you understand and can write your own animation experiments i'm going to create javascript class which is basically a template for creating objects classes encapsulate data and then work on it with their methods which is exactly what we will do today convention is the start class name with a capital letter to differentiate it from regular objects i will call my class for example flow field effect javascript doesn't really have classes like other programming languages do classes are just a syntax to simplify native javascript prototypes class is actually a special type of function and same as with other functions you can declare them in two ways this is class declaration and this is class expression both of these ways can be used to declare a javascript class important thing to note is function declarations are hoisted class declarations are not which means you can't call the class before you declare it javascript is changing every year a new syntax is being added one of the latest addition are so-called class fields which are basically properties that are held on your class usually here on the top and they don't depend on the constructor another new addition are private class features privacy could be applied to class fields or class methods by starting their name with a hash symbol until this change convention was to write names of private class features with underscore but these were still accessible from the outside they were still fully public with addition of private class features if you start name with a hash symbol privacy encapsulation of that field or method will be enforced by javascript and you will get a syntax error if you try to access it from outside the class the only way to get a value of private class field from the outside of class is to create a public method on that class that returns this private value i will show you how to do that since we will need to use all of these techniques to create animation loop and event listeners within this encapsulated code structure we will take it step by step and we will experiment a bit to see how everything works don't worry it's easy this course is beginner friendly i will create three private class fields ctx to specify what canvas element we want to draw on in case we have multiple canvas layers we can bring all of them here like this i will also create fields for width and height so that we are not taking these values from the outside we don't want to be using global variables inside our classes if we can avoid it we can do this two ways i can assign values to my private class fields directly in this area or i can do that inside my constructor i need these values to be passed as arguments to my constructor first so we will go with the second option this time let me show you constructor is a special mandatory method every javascript class needs to have it will just run once at the point when we create an instance of the class by calling it with the new keyword we will do that in a minute my constructor will expect three arguments coming from the outside ctx width and height inside the constructor i take the private class field from line 9 and i assign it to ctx that was passed to my constructor i do the same for width and height when i create an instance of this class in a minute i will pass it ctx from line 3 canvas width and canvas height as these ctx width and height arguments and constructor will convert these global variables into private class fields this is how you can create encapsulation in your project and avoid using global variables inside your classes because i could also be pulling ctx variable from line 3 into drawing code inside my class directly but that would be considered a bad practice doing it the way i'm showing you today is how animation libraries are made for example important thing to note about private class fields is that you first have to declare them in this area before you assign them values inside the constructor if i don't declare them here and just write this line i will get a syntax error you can execute code or even call class functions at the point when class is instantiated with the new keyword and constructor is called to create a new object let me show you what i mean i will put control lock inside my constructor that says effect loaded for example inside unload event i can create a constant variable i call for example flow field and i set it equal to new flow field effect like this the new keyword is a special command in javascript it will look for class with the same name and it will run its constructor once constructor creates one new blank javascript object and assigns it values and properties as declared in the blueprint here i can see my class constructor expects ctx width and height as arguments so i pass it ctx from line 3 canvas width as width and canvas height as height in console i can see the console log from line 17 was triggered so i know i successfully created an instance of my flow field effect class notice here we took ctx from line 3 and we passed it to our class constructor converting global variable into a private class field this is encapsulation one of the main principles of object-oriented programming encapsulation is the bundling of data and the methods that act on that data in a way that access to the data is restricted from outside the bundle so let's create a private class method that will act on our data i will call it draw and i start its name with hash like this this means this is a private method it cannot be called from outside this class it can only be called from somewhere inside this class usually to manage some kind of internal functionality such as calculating a value and so on in this case drawmethod's job will be to draw a single frame of my animated effect we will start simple to make sure it's very clear what's going on before we scale it up into a complex interactive animation at first the only job of this draw method will be to draw a single line and it will take two arguments coming from the outside for x and y coordinates for starting point of this line to start drawing a shape i take this.ctx class field from line 10 which holds a reference to my canvas drawing context and i call begin path built in canvas method on it this method simply tells canvas we wanted to start drawing a new shape if we were drawing a shape before this one begin path will close the previous shape and start a new one to draw a line on canvas is very simple we use built-in move to method to set starting x and y coordinates our line will start at x and y coordinates that were passed to draw method as arguments here then we call build in line 2 method which will set the endpoint coordinates of line we can also call a line two method many times to create a more complex shape like i did in a recent course to draw animated stars and polygons end point of this line will be the initial x position plus 100 pixels and the initial y position plus 100 pixels finally we call built in stroke method which will draw the actual shape we just mapped if i wanted the line to be drawn at the point where i create instance of my flow field effect class i can just call draw method inside constructor like this draw method expects starting x and y coordinates so i pass it 10 10. by default stroke style is set to black and we have black background so inside constructor i set stroke style to white it goes from starting coordinates in move to method two ending coordinates in line two method i can change these values however i want and the line will be drawn between these two points i could also create a variable called length i set it to 300 and i replace hardcoded values on line 25 with this new length variable now i can change the length of my line here we have built a solid boil plate to draw in codebase and we can do so many different effects and animations here now notice that i'm running private draw method by calling it from inside constructor which is being triggered here on line 6. i'm calling private draw method from class constructor which is public my public constructor enables access to private draw method from the outside right now it just draws a single static line what about if we want to make it move how do we create animation loop in a code base like this this time it will be a public method so it can be called from outside the class i call it for example animate i take draw method from line 19 and i put it inside animate i remove this console log inside animate i put another console log that says animating so again i'm calling private draw method from inside public animate method i could call animate from inside constructor to run it when class instance is created but i can also call it from the outside since it's a public method on line 6 i have my custom flow field variable that holds an instance of flow field effect class i can say flow field dot animate to call its public class method if i called flow field draw from here i would get a syntax error because a draw method starts with a harsh symbol it's a private method and privacy is enforced by javascript with this syntax private methods cannot be called from outside their class directly like this so we called animate and we can see the line i can also see console.log that says animating it is not actually animating we just called the method once and it drew a static line on canvas to animate something we need to be calling animate over and over to create animation loop the best way to do it is to use built in requestanimationframe method it takes one argument which is the name of the function we want to call i pass it animate the name of its parent function doing that will create an endless loop because we call animate first here on line 7 it will run its code and request animation frame will call it one more time it will run its code again and request animation frame we'll call it again we have an endless loop here javascript also has a built-in set interval method where you can give it a function and amount of milliseconds and it will call that function over and over in that interval request animation frame is better than set interval for creating web animations because it automatically adjusts itself to screen refresh rate in most browsers usually it runs around 60 times per second unless you have a high refresh rate screen if i run the code i get an error in console i can see my error is on line 30. it says animate is not defined it's because animate is a method on a class i need to refer to it with this keyword when i run the code again i can see we get one loop because it gives me one console lock but then on the second loop we get uncode type error cannot read properties of undefined online 28. we have a problem with this keyword we are calling animate on line 7 and on the second loop javascript forgets what this keyword represents it forgets that this keyword is our flow field object to make it remember which object this keyword is pointing to on consecutive runs of animation loop i need to bind it bind is a built-in javascript function which takes at least one argument and that argument sets what object this keyword refers to for that upcoming function call i'm basically saying call this.animate and remember that this keyword represents this flow field effect object so make sure you keep information about its properties and methods in memory for that upcoming function call scopes and closures are one of the most complex javascript principles so be patient and take your time it takes a while to fully understand it if you are a beginner if i check my console now i can see we have successfully created animation loop and it's running over and over drawing the same line on canvas 60 times per second when you animate something on canvas you basically draw it delete it update its position and draw it again this will create illusion of motion to prove that the line is animating i can for example create this dot x and the disk dot y properties and set them to zero i pass them to draw method on line 30 and for each animation loop i increase x by 2 pixels and y by 0.5 pixels and our line is animated i can change the values it looks like this because we see old paint we are not deleting old lines so it creates a path like this for our effect i only want to see the current animation frame so i will use built-in clear rectangle method to clear the entire canvas between each frame i want to clear the entire canvas area so from coordinates 0 0 to coordinates this dot width and distal height now we can just see the current animation frame when i resize browser window my black canvas doesn't automatically resize with it it stays the same size how do i make sure my canvas animation is fully responsive upon line 10 i will create an event listener for window resize event this event fires every time user changes size of browser window and it will run its callback function in the callback i have to set canvas width and height to the new window size simple when i trigger resize event i can see we have a scope issue because canvas variable is declared inside unload function if i put resize event listener on a global scope it won't be able to see it i can fix it for example by putting resize of a listener inside onload event or i can bring my canvas variable one level up on the global scope on line 1 i declare led variable called canvas and here i remove content keywords so we are no longer declaring new variable we are just assigning value to canvas variable from line 1. i create another led variable called ctx and i do the same thing now canvas is assigned to the correct canvas element only after the page has been fully loaded but at the same time it's globally available across my application as you can see resize event fires correctly and adjusts canvas width and height to the new dimensions as i'm resizing browser window it is also distorting and stretching my drawing so i have to create a new instance of flow field effect class since we are passing it width and height and those values have changed again i bring it one level up to a global scope first and here on line 10 i assign instance of flow field effect class to that existing variable from line 3. whenever i resize browser window i create a new instance of this class because i need to pass it the new updated canvas width and canvas height to make sure my animation is aware of what area of canvas is available for it to draw on and since this is a new instance with different values i need to call its animate method now i'm doing something very bad every time resize event fires i'm creating a new instance of flow field effect class and i'm calling its animate which has its own request animation frame method and it runs an endless loop i am now running multiple animation loops at the same time in console i can see the console log inside animate is being called a lot we need to cancel the previous animation before we create a new one we are creating animation loop here on line 46 with built-in request animation frame javascript method there is another method called cancel animation frame which simply cancels previously scheduled animation frame request all we have to do here is to make sure we pass a reference to it correctly so that it knows what to cancel i will hold that reference in a variable to make it very easy i create another global variable called flow field animation up on line 4 and down here on line 47 when i create animation frame request i assign it to this new variable if i run the code now everything still works the same doing this didn't break our animation loop good when resize event fires i take this flow field animation variable from line 4 which holds a reference to the next animation frame that is about to be drawn and i pass it to cancel animation frame method like this when we resize i cancel old animation request resize canvas to match the new window dimensions create new instance of flow field effect class and i call animate to create new request animation frame loop what if i want the line to start from exactly in the middle of the screen i will pass my draw method width divided by 2 and height divided by 2. i'm no longer using this.x and this.y so i delete this code and this code we are still animating a static line i could also use a little bit of trigonometry here starting x coordinate will be width of canvas divided by 2 so in the middle plus built-in market sign method which expects angle value in radians you can use method sign for any kind of wavy movement in your animation and game project all you have to remember is that math.sine expects a value of angle in radians as an argument and it will map that value somewhere along a sine wave if you pass it every increase in angle value it will create endless wave movement that goes back and forth between -1 and plus 1. i will pass it this.angle property and i create it on my class and for every frame of my animation loop i increase distort angle by 0.1 it just moves between -1 and plus 1 so the movement is hardly visible you can multiply it by radius to create a larger value range in your sine wave movement i can also add it to my start and y coordinate if i make the second call cosine sine and cosine will work together to map a circular path if you are beginner don't feel like you need to fully understand trigonometry for animation purposes just remember if you pass it ever increase in angle value it will map a wavy movement between minus 1 and plus 1 or whatever value you give it as radius here i'm using radius of 100 pixels we will also use trigonometry for rotation but i also want to show you how to make your animations interact with mouse position first on a very simple example and then we will take it to the next level and make it react to mouse in a big way where mouse position completely changes the final effect i create a constant variable called mouse and i set it equal to javascript object this object will have x and y properties which will represent current mouse coordinates first i set them to zero i create a mousemove event listener event listeners have access to a special built-in event object it's auto-generated every time the event fires and we can access it by giving it a variable name here i will call it for example e inside i console log this e variable first i will comment out the other console log we have on line 53 i will also comment out this line to stop animation for now right now if i move mouse over canvas area my mouse move event listener fires and it console looks this auto-generated event object i can open that object and i can see it has all kinds of different properties that give me very specific information about this particular tick of mouse move event what i'm interested in are these x and y properties every time a mouse move event ticks i will take mouse.x property from line 24 and i set it equal to x property on this event object we just inspected in console i do the same for mouse y from line 25 on line 53 inside animate method i remove math.sine and method cosine and inside the draw method on line 4d7 i set ending coordinates of the line to current mouse x and y positions now we have interactive line connecting middle of canvas to the current mouse position i can change some canvas settings for example line width could be 5 pixels if i comment out clear rectangle from 953 we can see old frames and we can draw shapes like this for the following effect i only want to see the current animation frame let's use clear rectangle again before we make our effect more complex very important thing especially if we have things moving around you don't want our animation to move fast on a new computer and very slow on older pcs just because the new computer is able to surf frames much faster we make sure animation runs at the same speed on each machine by measuring so called delta time delta time is the difference between the current and the previous animation frame in milliseconds to calculate it i will need a property that will temporarily hold a timestamp value from the previous frame i will call it for example this to last time to get a time stamp is very easy if you are using request animation frame in a similar way how event listeners automatically generate event object with information about that event request animation frame automatically generates a timestamp value that is being passed to the function we call usingrequestanimationframe in this case our animate method it's been automatically passed there and i can access it by giving it a variable name i will call it for example timestamp like this so this timestamp is telling me when was this particular animate method called by request animation frame to calculate delta time the difference between this frame and the previous frame in milliseconds i take timestamp from this loop minus timestamp from the previous loop which i will be saving in this dot last time property from line 43 so each time request animation frame serves the next loop of animate it passes it automatically generated timestamp i use that timestamp to calculate delta time by taking this current timestamp minus timestamp from the previous loop and when i have delta time value i override this dot last time from line 43 with the value of current timestamp to make it available to calculate delta time in the next upcoming loop if i control timestamp you can see it's given me value in milliseconds since the start of the first animation loop if i console delta time the difference in milliseconds between frames most modern computers will give you around 16.6 milliseconds which is 1000 milliseconds divided by 60 60 frames per second if your computer screen has high refresh rate you can get a lower delta time value here if you have old pc and your computer is struggling with animation especially later when we create more complex effects you might get higher value of delta time or you might get spikes it doesn't matter because if we have this delta time value we can tell our computer how many milliseconds we want to pass before the next animation frame is served if we have a slower computer frame rate might be lower but delta time will allow us to synchronize movement and animation speed so that timings are the same on all machines timestamp is generated when a function is called by request animation frame request animation frame inserts that timestamp as an argument to animate function so we have to keep in mind that only the second loop is triggered by request animation frame the initial first call of animate will not have timestamp argument passed to it so we will pass it 0 just for that first initial frame i delete this console log we can use delta time when something is moving around in a certain speed but also for something that happens in a certain interval periodically such as serving the next frame of our animation but also adding a new animated object or something like that something that happens over and over in a certain interval to use delta time this way we will need two helper variables one will hold an interval value of that periodic event in milliseconds i will call it this.interval and i want my repeating event to be triggered every 100 milliseconds the second helper variable will be a counter that will add delta time for each frame over and over starting from zero until it accumulates the amount in the interval variable at that point it will trigger the event and reset itself back to zero so that it can start counting again doing this will allow us to make sure that events in our codebase are triggered at the same interval let's say i have interval of 100 milliseconds on a fast computer delta time is 16 milliseconds so it will jump by steps of 16 milliseconds until it reaches 100 and it triggers the event somebody might have a slower computer their computer will have higher delta time for example 50 milliseconds because their computer is not able to serve animation frames so fast for some reason faster computer will have to tick six times run six animation loops before it reaches 100 milliseconds interval in 16 millisecond steps but slower computer will have to tick only twice to reach 100 because each step its delta time is 50 50 plus 50 is 100 this will even out the difference in their performance and in the end the repeating event in our code base will be triggered at the same or in the very similar time period so with this in mind i want to draw a new animation frame every 100 milliseconds i say if this.timer from line 45 is more than this.interval from line 44 only then run all this drawing code when you run it reset timer to 0 so that it can start counting milliseconds again for the next time else just increase this.timer by delta time and don't update my drawings this is periodic event based on delta time updating our animation every 100 milliseconds is just 10 frames per second it will be laggy i could also do 500 milliseconds which is just two frames per second thousand divided by two is 500 if we want smooth animation we set this interval to 60 frames per second thousand milliseconds divided by 60 which is roughly 16.6 milliseconds vector field usually looks like a grid where each cell has a line of certain length that points towards a direction i create a cell size property on my class which will represent how big individual cells in my grid will be i want grid with cells 15 times 15 pixels for example i rename my draw function on line 48 to draw line to more accurately represent what is the function doing i also need to update it on line 61. i delete the reference to this.angle here and here now i want to map a grid 15 times 15 pixels all over my canvas and inside each of these cells i want to use my draw line private class method to draw a smaller line we can create a grid on canvas with nested for loops which means loop inside of another loop the outer loop will go over canvas row by row from top to bottom it will represent vertical y coordinate of each cell we will start up top add vertical coordinate 0 and we will go down row by row until we reach height of canvas here i'm a mapping a grid so rather than going by one pixel each time we just jump to a new row we will jump by the amount of cell size from line 45 in our case 15 pixels so the outer loop will manage rows each time we enter a new row we will map horizontal cells there by going from left to right jumping by the amount of cell size again we will start at horizontal coordinate 0 and until we reach width of canvas we will be jumping by cell size 15 pixels inside we will call draw line from line 47 each time we enter a new cell in our new grid that line will start at horizontal x-coordinate of that cell with x-value coming from the inner loop and y-coordinate coming from the outer loop i want just a simple short line for now so let's say five pixels to the right and five pixels down from the starting position so what i just did here imagine these cells are larger just for the purposes of this visualization outer loop enters row with vertical position y we enter in a loop and we map horizontal positions on that row going from 0 to canvas width jumping by cell size of 15 pixels from left to right when we reach the end we exit the inner loop enter the outer loop y increases by 15 we enter in a loop again and we map that second row from left to right again we exit the inner loop y increases by 15 again and we map each row until y reaches the height of canvas so bottom of the page this is how you map a grid on canvas using a nested for loops i can change length and direction of lines by changing values here i can also change number of cells in a grid by changing value in the cell size property on line 45. direction of lines depends on x and y values we pass to line 2 method so as we said private class methods have names that start with a hash symbol they can be called from within the class to manage some kind of internal functionality i can't for example create another private class method to create gradient i call it create gradient for example inside constructor i create a property called this dotted gradient inside the new method i take this.ctx from line 37 and i call build in create linear gradient html canvas method it expects four arguments the first two are starting coordinates and the last two are ending coordinates of a line that line will represent size and direction of the gradient i want it to go from top left corner so coordinates 0 0 to bottom right corner so canvas width canvas height to map colors along the gradient we can use special built in add color stop method which can only be called on a variable that holds a reference to canvas gradient add color stop needs two arguments the first argument is offset and it's a value between zero and one it will define where along the gradient we want this color to be zero is a start one is the end it can be any value in between the second argument is the color so i have two color stops here the first will be at 0.1 offset and color will be ff5c33 the second color is at offset 0.9 and i will use fff ffff33 now i can call this custom private create gradient method from line 49 inside my class constructor which will execute it as soon as i instantiate flow field effect class with the new keyword i'm getting this error in console because i didn't put hash symbol in front of hexadecimal color value here when we created our gradient we can take stroke style and assign it to this dot gradient from line 45. i can add more color stops i'm gonna make additional color stops at 0.2 0.4 0.6 and 0.8 and i will intentionally go for lighter colors since i want them to contrast against the black background i can change line width and i can play with cell size to create simple patterns like this i can also pass additional angle property to draw a line on line 73 and i need to make sure drawline function expects it as an argument on line 58. that angle will be coming from my for loops here i can for example calculate it by calling mastered cosine and passing it x from the inner loop plus market sign and passing it y from the outer loop then we call draw line passing it angle and inside the draw line i will use that angle to change ending coordinates we pass to line two method by default these are very small values so let's try and multiply them by radius of one hundred maybe twenty i can also multiply it by some random number here just to see how it affects the pattern to actually make each line rotated based on its x and y position i can actually just say x plus math or cosine and i pass it angle and y plus math.sine and i also pass it angle again these are very short lines so i multiply them times 20 for example now we are starting to get more interesting pattern originally i was using built-in translate and rotate canvas methods to achieve the same thing but thanks to my friend radu we found out that replacing translate and rotate with sine and cosine and passing into the same angle value will also result in rotation effect but it's much less performance demanding so it will run smoother when we animate it in a minute radu is an expert on algorithms check out his channel i will link some of his interesting project tutorials in the video description so to make it clear what's going on by including x and y position in sine and cosine the lines rotate around based on their position as we go from left to right and from top to bottom lines are slowly rotating and it creates a pattern as a result if i multiplied by a smaller number we get less angle rotation between each neighbor in line which affects the size and resolution of the emerging pattern now we have something that could be called a vector field each cell in the grid has a line with certain length and direction now i could just throw some animated particles on canvas and make them flow around following these vectors these lines speed and direction of movement of each particle would depend on length and direction of line that the particle is currently flowing over and that is the principle of flow fields right now we are creating a regular circular repeating pattern with trigonometry you can also use more complex algorithms than the one we just used for example very common one used here is perlin noise or simplex noise this will give you random but very natural looking flow pattern values here on line 73 affect kind of how close we are zoomed in to the pattern and also ratio between its vertical and horizontal scale changing this radius value on line 62 changes the length of lines now the lines are longer than individual cells so we are getting some areas with overlap making the pattern slightly more prominent i can zoom into the pattern by multiplying by a smaller value here or i can zoom out by multiplying by larger decimal values i can also wrap this whole block in brackets and multiply that by some radius which will affect how much is the emerging pattern curving in on itself let's try to change line width smaller cell size will give effect more detail but also it's more demanding on system to draw so keep that in mind if at any point from now you start getting performance issues because we are drawing a lot of lines making this cell size value a larger number is the easiest way to increase performance and render speed of your effect anything over 15 pixels should be safe anything under that value and you might notice some fps drops i think we will see when we start animating and moving this effect around based on periodic events and user input definitely don't try to animate it with 3 pixel cell size rendering this as a static image would be fine but drawing updating and animating each line like this 60 times per second will be a challenge for most computers if you are having performance issues right now increase the cell size value until your computer can handle it let's experiment some more look how this radius value changes how the emerging pattern curves in on itself when i slowly increase it do you see what's going on what if i want to make this movement automatic and i want it to slowly animate like this on its own i increase cell size to something safer for example 15 pixels i set line width to 1 pixel line width also affects performance with some effects on canvas bigger and thicker lines take more system resources to draw so keep that in mind i replace this hardcoded value we just played with with a variable i will call it for example this dot radius i declare it as a property on my class constructor initial value can be 10 or maybe 5. i want this radius value to automatically increase by very small amount for each animation frame i create additional property called this.vr velocity of radius and i set it to 0.03 initially i set radius to 0. inside my animate method i increase this.radius by distort vr from line 49 for each animation frame now we can properly see how this value makes this pattern spiral inside and create more curves keep in mind that all we are looking at right now is just a grid of lines that are rotating around based on their vertical and horizontal position in areas like here where there isn't much overlap it's very clear to see what exactly is each individual line doing i can reduce cell size to make the effect look more detailed but be careful here don't use very small numbers here also size of our canvas will affect performance in this case since larger canvas means more lines which means more calculations and draw calls that needs to happen per frame at a certain point i want the spiraling to stop and start going in the opposite direction so that we have effect that endlessly bounces back and forth between a certain value range this following technique can also be used for bouncing movement or whenever you want something to go back and forth between two values we are adding this dot vr to radius endlessly on line 72. i can simply say if we reach a certain range limit flip it to the opposite value so if it's positive make it negative if it's negative make it positive since we are adding vr here if vr becomes negative it will start going in the opposite direction i say if this dot radius is more than 5 or if this.radius is less than -5 flip this dot vr to its opposite so from positive to negative or from negative to positive by multiplying it times -1 when we reach the limit both maximum or minimum limit it will flip the direction by multiplying it times minus one the correct syntax here is actually times equals minus one so now we go all the way to plus five then it flips ghost path zero to minus value when we reach minus five we flip again and now we have endless animation bouncing between minus five and plus five this hard-coded value of 30 is actually length of lines so let's use my length variable here instead if i set length to 10 the lines are very short i increase cell size to 15. line length to 15 as well so now it's the same as cell size length 20 and we are starting to get some overlap and the swirly pattern becomes more visible we can also make line length dependent on some variable input for example on position of the line or even based on current distance of that particular line from the mouse cursor that could look interesting let's try that i create a custom temporary variable i call for example position x and i set it equal to x passed on line 60 as an argument and i do the same for position y we already created mouse object that has x and y properties based on the current mouse coordinates i want to cycle through all my lines in this effect and calculate their current distance from mouse cursor to calculate distance between two points is easy with pythagoras theorem formula we have two points one point is mouse cursor the other point is starting x and y coordinates of a line to calculate the distance between these two points i calculate their distance on x-axis and y-axis separately which will give me this imaginary right triangle since i know this side and this side and i know this is 90 degrees i can use pythagoras theorem to calculate this side which is the actual distance between the two points what does that look like in javascript first i calculate this side distance between mouse and start position of line on horizontal x axis dx distance x then i calculate d y distance between mouse and start position of line on vertical y axis that gives me two sides of right triangle and i know the angle between them is 90 degrees pythagoras theorem gives us a formula to calculate hypotenuse the longest side of the right triangle which is at the same time the distance between my two points mouse and starting point of the line the formula is square root from dx times dx plus dy times dui or dx squared doi squared since i need to calculate this distance between mouse and each individual line in my effect for each frame of animation i will skip the square root calculation because calculating square root is a very performance demanding operation it will still work we will just have to work with much larger numbers since we are not square rooting them so we have distance between mouse cursor position and position of each line it is held just temporarily here and we will use it to draw lines of different lengths i do that by assigning distance to length length is used here to draw lines if you remember that will give us extremely long lines we need to make it a much shorter length could be for example distance divided by 10 000. i have to use large numbers like 10 000 here because we skipped square root also i'm dividing it by 10 000. maybe a better idea would be to multiply it by a very small number again for performance reason multiplication in javascript is more efficient than division this is starting to look really interesting we have a grid of lines that are rotating in a pattern based on their x and y position in the grid they are auto rotating within a certain range back and forth and at the same time they react to mouse movement i hope you are getting some value since these creative coding techniques can be used to create thousands of completely different effects we covered a lot today but we are not done yet let's take it another step further and make it even more reactive first i will cup the minimum and maximum length of lines because on large screens the distant lines can get very long covering and interfering with the rest of the effect i will just do a simple if statement here if distance is more than 150 000 keep it at 150 000 this will not allow it to go any higher okay that's too short since we are then dividing that value maybe 300 000 still too short 400 500 that's all right also i do else if to cup minimum length you don't have to do that if you want your lines to be extremely short around the mouse i'm just experimenting now i said minimum value two hundred thousand maybe maximum to six hundred thousand yes that's better and minimum fifty thousand look at these two values here they affect how zoomed in or out we are on the pattern and ratio between these two numbers determines how stretched the pattern is horizontally or vertically if i keep them the same and use smaller number we zoom in if we multiply by a larger number we zoom out of the pattern a little bit what happens if i include mouse x here and mouse y here now these numbers are very large and it's making the angle values too large and lines rotate so much the pattern is lost i can scale it down by multiplying it by much smaller numbers now line length reacts the distance from mouse and at the same time we are scaling and stretching the effect based on mousex and y position this feels very interactive if anything it really shows you how we can zoom in and out of the pattern and how it can be stretched and scaled vertically and horizontally we could also attach these values to sliders for example to generate certain more static setup if that's what you want you can do that as a challenge if you think you're ready and if you don't think you're ready you can always watch more of my videos and get more comfortable with canvas in javascript i disable the automatic radius animation and i set radius to a static value of 3 for example or maybe 6. now the pattern itself is stable and we can better see how going from top left to bottom right zooms on it and how stretching it vertically and horizontally affects the final outcome this is a lot of interactivity i uncomment the automated radius it's stuck now because i set the initial radius to be outside the range we defined on line 80. easy fix if you are a beginner you should be really proud of yourself right now we used so many animation techniques today and we went really far from drawing a single line on canvas you can do so many other different animations and effects with this code base for example flow fields if you are curious to see what else is possible with built-in canvas drawing methods check out my playlists i'll see you there
Info
Channel: Franks laboratory
Views: 31,616
Rating: undefined out of 5
Keywords: html canvas, html canvas for beginners, html canvas tutorial, html canvas deep dive, html canvas crash course, HTML5 Canvas, HTML5 Canvas Crash Course, HTML5 Canvas CRASH COURSE for Beginners, HTML5 Canvas for Beginners, vanilla JavaScript, front end web development, html5 canvas tutorial, drawing on the canvas, Learn HTML5 Canvas, HTML5 Canvas API, web development, html5 canvas animation, html, html5, javascript, franks laboratory, frankslaboratory, html canvas basics
Id: uCH1ta5OUHw
Channel Id: undefined
Length: 49min 43sec (2983 seconds)
Published: Fri Oct 08 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.