Learn HTML Canvas: Pixels & Physics

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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 canvas 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'll add some amounts 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 we start by creating three files index.html style CSS and script.js 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 emit extension so I can just press exclamation mark and tap key and I get a basic web page boilerplate like this this video is a HTML canvas introduction course but I expect that you know some basics of JavaScript I give my web page some title for example awesome JavaScript effects I link my style CSS file like this I create HTML5 canvas element with an ID of canvas 1. 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 are the 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 in this course we will explore what can be done with images and we will go from Basics to some really cool Advanced effects hope you have fun I create a div element with a class of controls and inside we will have a button with an ID 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 so this project will also contain some 2D 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.js file down here at the bottom to link our JavaScript logic in style CSS I started with a 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 0 and padding to zero canvas with an ID of canvas 1 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 div with a class of controls will be set to position absolute this will remove it from the document flow and place it over the canvas element we need to make sure that the buttons it will contain are not covered by anything and are always clickable so I set it set index to 100 putting it all the way up top in front of all the other elements I go back to index.html and I create an image element I give it an ID of image and 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 online 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 base64 string that line of code contains all the image data completely replacing the PNG file we have here I can convert my image into base64 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 to base 64. I select the first link from onlinepng tools.com if you want to use the same images I'm using you can download them in the video description these 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 base64 code for me for the code to work I need to make sure the string starts with the data colon image PNG base64 like this if it doesn't start like that for you just make sure you take this checkbox so 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 the string and draw the image from it if 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 base64 string and I copy it in index.html I place my mouse cursor 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 base64 string contains the entire image no need for the actual PNG file and 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 so 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 simpler 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 the JavaScript this code base has many improvements and optimizations you will see the bigger image you use the longer bass 64 string you will get so instead of pointing image source directly to a PNG file we converted our anglerfish 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 so I will take IMG tag and I give it a display none the image is hidden but we can still access it with JavaScript to draw it on canvas when we need it it's still sitting here in index.html in script.js 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 stylesheets 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 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 kind of a setup I create a constant variable I call for example canvas and I point it towards the HTML canvas element we created in index HTML file by using get element by ID we give it an ID of canvas 1 like this a variable I call CTX shortcut for context is the disc canvas variable from line 2 dot get context get context is a special method that can only be called on a variable that holds a reference to HTML5 canvas element get context method expects an argument we call context type we can pass it 2D or webgl today we will work with 2D when called like this get context method will create an instance of an object called canvas rendering context 2D this object contains all canvas settings and all built-in canvas drawing methods we will need today I can consolock CTX to have a look at it I can see it right here and 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 10 pixels sound serif and that default line width is one pixel we can override any of these settings by assigning them to a different value with JavaScript and we will do that later if I click on the Prototype here and expand it we can see all the built-in 2D 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 any 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 console log and close the browser console I take canvas variable from line 2 and access its width property I set it to window in a 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 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 fog 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 the image 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 and 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 simplicit class is a template and 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 will need here is animation Loop so 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 A Constructor method this is a special kind of method and 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 so let's define that blueprint let's define the properties of our particles every particle will be a piece of an image a pixel so 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 JavaScript knows where on canvas to draw it on HTML canvas same as in web development in general we have horizontal x-axis going from 0 pixels on the left and increasing as we move right we also have a vertical y-axis starting at the zero pixels up top and increasing as we go down so for example position 5100 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 to coordinate 0 0 so top left corner on canvas when we define properties on JavaScript class we say this dot 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 so this time we will be able to draw more particles efficiently size of each particle rectangle will be 3 times 3 pixels foreign foreign all we have to do is take CTX variable from line 3 that holds an instance of canvas 2D drawing API and we call built-in fill rectangle method like this it expects four arguments X and Y position where to draw it and width and height of the rectangle it will fill that rectangle with color if we don't Define fill style we know that default fill 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 it's 100 pixels wide and 200 pixels tall if this is your first canvas project you should maybe pause 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 so let's bring our anglerfish image into the project and draw it on canvas I create a constant variable I call Image one I point it towards the image element using its ID image 1 which I gave it here now I can take CTX variable from line 2 which as we said holds all canvas properties and methods and 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 1 from line 7 and I want to draw the image from the top left corner of canvas so from coordinates 0 0 if I give it position of x 100 we push the image 100 pixels to the right if I pass it 100 pixels as vertical y coordinate I push the image 100 pixels down I can also pass it optional width and height arguments if we don't pass width 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 pause the video and pass the different values for X Y width and height so that you can see how it affects the position and size of the image so now we know how to set up a HTML canvas 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 the 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 3 and from it I call built-in fill rectangle method we already learned that fill rectangle expects four arguments X Y width and height so that JavaScript knows where to draw that rectangle and what size it should be I pass it this dot X from line 11 this.y from line 12 as X and Y coordinates and I pass it this dot size from line 13 as with and the same digital size property as height like this so we defined 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 1 and I set it equal to new particle like this the new keyword is a special command in JavaScript it will look for a class with that name it will find that class here on line 9 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 1 object was created using this particle class it has access to draw method we defined on line 15 and we can simply call it like this particle 1 dot 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 foreign more than one particle I can just repeat this code and create variables for particle 2 particle 3 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 so we can use this effect class we defined on line 20 to create and handle multiple particles so particle class will handle individual particles effect glass will handle the entire effect all the particles at once I give it a Constructor and 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 with 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 9. I will give my effect class a custom method I call for example init 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 dot 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 9 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 a 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 es6 syntax here this is so called Ro function right 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 and on each of those called their Associated draw method from line 15. so we have effect class with init method to fill particles array with particle objects and draw method to draw all of them this is a draw method on line 29 draws all the particles the entire effect another draw method online 15 specifies what will each particle look like in our case we decided to draw them as simple block 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 so I pass it canvas width from line 4 and canvas height from line 5. let's control 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 init 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 control lock is here to help us now I can take effect variable again and I call draw method from line 29 I also delete the console log nice we are drawing one particle using our new code structure I can move the particle around by changing these values I'm doing something bad here we are pulling CTX variables from line 3 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 fill rectangle method from it I will pass CTX variable from line 3 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 and we pass that reference along the draw method on particle class that reference will be passed here and from there we call fill rectangle doing it like this will make sure our code is modular it's considered a good practice rather than what we had before we can also randomize particle positions rather than hard-coding the coordinates I can set horizontal X position to a random number between 0 and canvas width vertical y position could be a value between 0 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 init method I create two particles by copying this line nice now we have two particles and as we create them using the new keyword every time the Constructor runs on line 10 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 with and canvas height to 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 this entire thing as an argument inside I convert that reference to a Class Property called this dot effect keep in mind that objects in JavaScript are so called to 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 so 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 this dot effect dot with 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 this dot 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 it too much if you are struggling you will become good at this as you write more object-oriented code bases 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 with to the effect class on line 35. converting it to this dot with 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 for Loop I delete all this code and instead I will wrap this in a for Loop like this index will start at zero as long as index is less than 10 increase index by 1 and create one particle this for Loop will run 10 times and create 10 particles for Loops are great to help us reduce code repetition let's also draw an image but because I don't want to be pulling this image 1 variable into my classes from the outside let me just turn it into a class property on the effect class like this I will call it this dot image we can draw it by taking context and call and draw image on it I passed it this dot image from line 24 and 0 0 for X and Y coordinates and we are drawing our fish on canvas again but this time we are doing it from a self-contained object-oriented code base 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 canvas I will create a helper variable called this Dot Center X it will be this dot width from line 21 so the width of canvas times 0.5 middle of canvas horizontally this dot Central Y is this dot height from line 22 times 0.5 the middle of canvas vertically I can use these new properties as X and Y coordinates past the draw image method and now the image is drawn from the point on canvas that is exactly in the middle however the image is not centered this dot image is a reference to IMG element we created in index.html let me console log it this image element has automatically generated width and height properties if icon to lock this dot image.width you can see it it says 500 pixels this dot image.height is 369 pixels now that we know this we can delete the console log 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 this.x on effect class in this case this dot X means horizontal position of the fish image it will be this Dot Center X from line 25 so the middle of canvas area horizontally minus half of image width now I can pass this dot X to draw image method on line 37 and the image is centered horizontally let's do the same for vertical y position this Dot Center y minus this dot image dot height times 0.5 and I replace it here so 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 so we are drawing our fish as an image and we are drawing 10 particles let's make it 100 particles and let's make each particle a square 5 times 5 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 this particles move we will handle that logic here inside update method I will need a new property called the VX velocity X which is basically a horizontal speed let's set it to 1 1 pixel per animation frame for now I also create velocity Y and I set it to plus 1 as well inside update method for each animation frame we want to increase horizontal X position from line 10 by the value of VX from line 13. we also want to increase position y by the value of vui so 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 so inside I take particles array and for each particle object in the array I call its update method we just 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 a request animation frame a 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 so when I pass it animate 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 and the number of callbacks 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 you to clear canvas from calling at 0 0 and I want to clear the entire canvas so I take values from up here so the width of the cleared area will be kind of a Swiss and 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 you 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 between 0 and 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 -1 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 the 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 VY 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 followed this 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 code in 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 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 and 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 a method that 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 this.x from 933 and this.y from line 34 Define horizontal and vertical position of the fish image we are drawing on canvas I can prove that by increasing this dot X by 1 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 initialize 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 online 48 down here and I need to make sure I pass it in that context so CTX variable from line 3. and it gets called only once on the first page load here so 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 initialize method so 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 init 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 canvas 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 a form of a special image data object it needs four arguments to specify which part of canvas we want to analyze I want to analyze the entire canvas element so from coordinate 0 0 to the width of canvas so this dot width from line 27 and the height of canvas so this dot height from line 28. the important thing to mention here is that get image data has 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 base64 string doing that the image itself becomes a part of index.html file and we can avoid this tainted canvas warning let's control 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 uint8 clamped array it's an array of unassigned 8-bit integers clamped to a range between 0 to 255. if you worked with web colors before you know that every color can be expressed by RGB and Alpha value so called rgba color declaration we use it in CSS all the time in this case red green and blue can be any value between 0 and 255. in CSS Alpha opacity is a value between 0 and 1. here in this array Alpha is also a range between 0 and 255 where 0 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 4 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 get image data from the top left corner that means that this top left corner pixel has a zero right zero green zero blue and 0 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 between 0 and 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 big red square as a visual help imagine the Red Square being 1 times 1 pixels in size I delete the console log so we drew image on canvas and we analyzed its pixel data now we have color of each pixel on canvas store in this pixels variable I want pixels variable to point to the array directly not to the entire image data object so I say dot data pointing towards this unsigned 8-bit clamped array now I will cycle through that array row by Row from top to bottom and 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 for Loops nested simply means it's a loop inside of another loop the outer for 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 wanted to decrease resolution a little bit so instead of jumping by one pixel when we finish a row we will jump by a certain value I call for example 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 so 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 so I set Gap to for example 5 pixels and every time we finish going through one pixel row we jump by 5 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 for 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 X 0 5 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 5 and we are on another row we enter the inner loop and again we go from left to right 5 pixels at a time when we reach the end again we will exit the inner loop enter the outer loop y increases by 5 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 and 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 so we are going through the entire canvas row by Row in 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 0 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 pixels 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 is y from the outer loop so 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 and then we add the leftover horizontal pixels depending how far along we are in the new row so 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 4 because we know that in the upside 8-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 and we want that actual array index as we jump from Pixel to pixel so 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 coordinates if you still don't fully understand it feel free to re-watch 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 so we know that red pixel value is the first one so pixels array from Line 39 and current index in that array we are currently on like this we know that the green value for that same pixel is next so index Plus 1. then there is an integer representing blue value index plus 2 and we have Alpha opacity index Plus 3. 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 open in bracket in quotes like this plus red from line 43 plus coma plus green plus coma plus blue plus a closing bracket the thing is we don't really care about transparent pixels so 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 anglerfish image the pixels around are wide because the body of the website behind it is white the canvas itself is transparent in these areas so its 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 column built-in array push method and we push one new particle object created by our custom particle class from line 7 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 so inside initialize 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 mastered floor to round them down just to make sure we don't get any sub pixel values and canvas doesn't have to use anti-aliasing to smooth the image out it's good to Mark the floor everything we draw on canvas for performance reasons this dot color will be color passed as an argument from here it will be comment from that RGB color we concatenated a while ago on line 50 so that each particle retains the original color of the image okay so now particle class Constructor on line 8 expects effect x y and color as arguments I go down inside our main effect class again and 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 for loop as horizontal X position y from the outer for loop as a vertical y position and color and that's how you analyze image for Pixel data and convert it into a particle system sorry 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 just a recap initialize 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 for 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 5 times 5 pixels over the entire canvas we extract red green blue and Alpha values and 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 init method we'll run just once on the first page load let's control log 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 and inside I see particles array we defined on line 32. it contains 2775 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 5 times 5 pixels defined in this dot 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 let's uncomment 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 and this dot y to Y 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 fill style to this dot color like this nice the fish is breaking into pieces because of VX and VY properties I set vx to 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 demand in this effect will be Gap is 5 pixels and I'm drawing particle blocks of 10 times 10 pixels so there is a lot of overlap I can set it to 5 or when I do 4 there will be 1 pixel gaps in the grid we can make it Dynamic by saying this Dot effect.gap that way the size of particles will change automatically as we change this.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 versions 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 3000 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 we can give it physics and behavior for some really cool effects I set Gap to 3 pixels for now our consulate particles array from line 33 by saying effect dot particles array like this because I set Gap to 3 pixels we get 7557 particles that make up the fish image 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 dot X position plus equals the difference between origin X position which is the particle's position in the image and its current horizontal position this dot y plus equals this dot origin y minus this dot y so 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 and 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 so this dot X will be method random between 0 and canvas width so times this dot effect dot width to actually see the movement I need to make sure that individual stat per frame is just a small fraction of the difference between the current X position and the origin X position like this I do the same for this dot Y and I set the initial vertical position to a random value between 0 and this dot effect dot height which is the height of the available canvas area let's do very simple physics I will call it ease 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 ease 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 vanilla JavaScript 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 set starting X to 0 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 even 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 let's create a button that when clicked will 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 the swap message runs horizontal x coordinate of each particle will be randomized between 0 and effect width and Y will be a random value between 0 and effect height like this I will also give it a different ease value just to experiment with it I go to the main effect class and the same as we did with the draw and update I create a warp method and when triggered it will cycle through particles array and 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 base64 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.js 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.getelementbyid 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 set an E's to a different value now your users can trigger this cool 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 10 pixels but in 10 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 swapped 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 button 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 Mouse I want the main effect class to always be aware of the current position of the mouse cursor I create a custom distort Mouse property it will be an object with three properties radius I will initially set to 3000 it will Define the area around the cursor where particles react to Mouse it won't really be 3000 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 Mouse move 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 Mouse X and mouse y properties as Mouse move 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 JavaScript bind method or I can change the Callback function into an es6 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 so it will always be able to see this.mouse property which is exactly what we need here we need access to the auto-generated event object since this Arrow function will have only one argument I can even remove the brackets like this whenever Mouse move event fires I take this.mouse.x from line 47 and I set it to event.x current horizontal coordinate of the mouse cursor I will also set this.mouse.y from line 49 to event.y like this Let's test if it works by console login mouse.x and mouse.y because I put that Mouse move event listener inside class Constructor of effect class it will get automatically applied when we instantiate effect class here on line 86 I save my changes refresh my browser window and now when I move Mouse over canvas I can see Mouse position is updating and updated values are being correctly saved in our custom desktop Mouse property on effect class perfect let's delete the console log on line 88 and also this one on line 54. it's good to always delete console logs like this for performance reasons so 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 and 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 DX distance on the horizontal x-axis between Mouse and this particle is this Dot effect.mouse.x from line 50 here minus this dot X this dot d y distance between Mouse and particle on vertical y-axis is this dot effect.mouse.y minus this dot y to calculate a distance between two points we can use Pythagorean 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 DUI this stands 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 Pythagoras Theorem to calculate hypotenuse the longest side of a triangle opposite to the right angle Pythagoras Theorem formula is C is equal to square root of a squared plus b squared convert it into JavaScript we say this dot distance is math.s square root from this.dx times this dot DX which is a squared plus this dot d y times this dot d y b squared that gives us the distance between two points between particle and mouse I'll 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 we are not using square root here that's why I set Mouse radius to 3000 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 helper property called this.force it will be a radius of the mouse so 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 so 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 cursor I check if this tense is less than Mouse radius and 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 this.dx this dot DUI digital distance and this dot force all these properties will be zero at first I will also create another property I call for example this dot angle this dot angle will determine the direction at which the particles are being pushed away when they interact with mouse so down here we will calculate that angle using math.a time 2 built in JavaScript method math.a time 2 method returns a numeric value in radians between minus pi and plus pi we know that circle is 2 pi in radians this value that method 8 and 2 returns is representing so-called Theta angle between a given X and Y point and positive x-axis so 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 so the difference between X and Y position of the particle and the X and Y position of the mouse cursor represented by this dot Dy and this dot DX passed the math.a time 2 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 and to specify which direction they are being pushed I pass this dot angle we are calculating on line 36 to master cosine method this method will map that angle we pass to it in radians as a value between -1 and plus 1. 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 this a Time 2 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 will play with 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 trigonometry logic it doesn't affect your ability to be a good JavaScript developer so 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 VY vertical speed but this time we will use math.sign we pass it the same angle method cosine and method to sine work together to map a position along the radius of a circle now I can use VX and vui vertical and horizontal speed in X and Y position calculated here like this let's try plus first to see what it does well 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 this dot friction I set it to 0.95 and I multiply VX 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 DX times the DX plus d y times d y like this Pythagoras 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 ease 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 JavaScript and drawing on canvas you can use these smart 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 Plus to minus swap sine and cosine try to break it and see how it affects the movement I hope it will make it more clear to see what is happening look 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 JavaScript 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 10 is very difficult 5 is something in between I'll see you later [Music]
Info
Channel: Franks laboratory
Views: 129,405
Rating: undefined out of 5
Keywords: creative coding, crash course, for beginners, creative coding for beginners, HTML5 Canvas, HTML5 Canvas for Beginners, HTML5 Canvas tutorial for Beginners, html5 canvas basics, particle systems, vanilla JavaScript, front end web development, html canvas tutorial, html5 canvas tutorial, Learn HTML5 Canvas, HTML5 Canvas API, HTML5 Canvas and JavaScript, web development, html5 canvas animation, html, html5, canvas, javascript, franks laboratory, frankslaboratory
Id: vAJEHf92tV0
Channel Id: undefined
Length: 77min 0sec (4620 seconds)
Published: Mon Sep 26 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.