--- tags: ironhack, lecture, --- <style> .markdown-body img[src$=".png"] {background-color:transparent;} .alert-info.lecture, .alert-success.lecture, .alert-warning.lecture, .alert-danger.lecture { box-shadow:0 0 0 .5em rgba(64, 96, 85, 0.4); margin-top:20px;margin-bottom:20px; position:relative; ddisplay:none; } .alert-info.lecture:before, .alert-success.lecture:before, .alert-warning.lecture:before, .alert-danger.lecture:before { content:"👨‍🏫\A"; white-space:pre-line; display:block;margin-bottom:.5em; /*position:absolute; right:0; top:0; margin:3px;margin-right:7px;*/ } b { --color:yellow; font-weight:500; background:var(--color); box-shadow:0 0 0 .35em var(--color),0 0 0 .35em; } .skip { opacity:.4; } </style> ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png) # Canvas | Styling, Text and Images ## Learning Goals After this lesson you will be able to: - Understand how to add styles and colors to the shapes - Learn methods to add text to `canvas` elements - Learn how to add different image formats such as **JPEG**, **PNG** or **GIF** to `canvas` ## Introduction ## Colors :::info lecture - couleur de contours : `strokeStyle` - couleur de remplissage : `fillStyle` ::: In the earlier lesson we saw some styling already, but now we will see a bit more examples. As we saw, there are two important properties we can use: `fillStyle` and `strokeStyle`. :::info **`fillStyle` = color**; <== Sets the style used when filling shapes. **`strokeStyle` = color**; <== Sets the style for shapes' outlines. ::: `color` is a string representing a **CSS <color>**, a **gradient object**, or a **pattern object**. By default, the stroke and fill color are set to black (CSS color value #000000). :::warning lecture Une fois setté, `ctx.fillStyle`/`ctx.strokeStyle` reste pour les suivants. ::: :::info When you set the `strokeStyle` and/or `fillStyle` property, the new value becomes the default for all shapes being drawn from then on. For every shape you want in a different color, you will need to reassign the `fillStyle` or `strokeStyle` property. ::: ### `fillStyle` example On the following example, we are creating a 6x6 grid, filled with squares of 25x25px. Every time we create a new square we are reassigning the `fillStyle` property, so we can get this effect. :::info lecture Ici on utilise une valeur de `fillStyle` en `rgb()` : ::: <iframe height='265' scrolling='no' title='A fillStyle example' src='//codepen.io/ironhack/embed/BdqwWQ/?height=265&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen <a href='https://codepen.io/ironhack/pen/BdqwWQ/'>A fillStyle example</a> by Ironhack (<a href='https://codepen.io/ironhack'>@ironhack</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> ### `strokeStyle` example This example is similar to the one above but uses the strokeStyle property to change the colors of the shapes' outlines. We use the arc() method to draw circles instead of squares. :::info lecture Idem ici, une valeur de `strokeStyle` en `rgb()` : ::: <iframe height='265' scrolling='no' title='A strokeStyle example' src='//codepen.io/ironhack/embed/ayRLVM/?height=265&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen <a href='https://codepen.io/ironhack/pen/ayRLVM/'>A strokeStyle example</a> by Ironhack (<a href='https://codepen.io/ironhack'>@ironhack</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> ## Transparency In addition to drawing opaque shapes to the canvas, we can also draw semi-transparent shapes. This is done by either setting the `globalAlpha` property or by assigning a semi-transparent color to the stroke and/or fill style. :::info **`globalAlpha` = transparencyValue** Applies the specified transparency value to all future shapes drawn on the canvas. The value must be between 0.0 (fully transparent) to 1.0 (fully opaque). ::: The `globalAlpha` property can be useful if you want to draw a lot of shapes on the canvas with similar transparency, but otherwise, it's generally more useful to set the transparency on individual shapes when setting their colors. Because the `strokeStyle` and `fillStyle` properties accept CSS rgba color values, we can use the following notation to assign a transparent color to them. ```javascript ctx.strokeStyle = 'rgba(255, 0, 0, 0.5)'; ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'; ``` ### `globalAlpha` example :::info lecture Il est possible de setter la transparence de maniere générale grâce à `ctx.globalAlpha`. ::: :::info lecture Cependant on se servira davantage de valeurs en `rgba()` ::: In this example, we'll draw a `background` of four different colored squares. On top of these, we'll draw a set of semi-transparent circles. The `globalAlpha` property is set at 0.5 which will be used for all shapes from that point on. Every step in the `for` loop draws a set of circles with an increasing radius. The final result is a radial gradient. By overlaying ever more circles on top of each other, we effectively reduce the transparency of the circles that have already been drawn. By increasing the step count and in effect drawing more circles, the `background` would completely disappear from the center of the image. :::info lecture Dans cet ex, on trace d'abord nos rectangles, puis on set globalement la transparence à `0.5` afin de dessiner nos cercles. ::: <iframe height='265' scrolling='no' title='A globalAlpha example' src='//codepen.io/ironhack/embed/rzqGZP/?height=265&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen <a href='https://codepen.io/ironhack/pen/rzqGZP/'>A globalAlpha example</a> by Ironhack (<a href='https://codepen.io/ironhack'>@ironhack</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> ## Line styles There are several properties which allow us to style lines. Let's check how to implement them. :::info - **<b>`lineWidth`</b> = value** Sets the width of lines drawn in the future. Values must be positive numbers. By default this value is set to 1.0 units. - **`lineCap` = type** Sets the appearance of the ends of lines. There are three possible values for this property and those are: *butt*, *round* and *square*. By default this property is set to *butt*. - **`lineJoin` = type** Sets the appearance of the "corners" where lines meet. There are three possible values for this property: *round*, *bevel* and *miter*. By default this property is set to *miter*. ::: ### Line Dashes The setLineDash method and the lineDashOffset property specify the dash pattern for lines. The setLineDash method accepts a list of numbers that specifies distances to alternately draw a line and a gap and the lineDashOffset property sets an offset where to start the pattern. :::info - **`lineDashOffset` = value** Specifies where to start a dash array on a line. - **`getLineDash()`** Returns the current line dash pattern array containing an even number of non-negative numbers. - **`setLineDash(segments)`** Sets the current line dash pattern. ::: ### Line Styles example <iframe height='265' scrolling='no' title='A lineJoin example' src='//codepen.io/ironhack/embed/wqYrbp/?height=265&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen <a href='https://codepen.io/ironhack/pen/wqYrbp/'>A lineJoin example</a> by Ironhack (<a href='https://codepen.io/ironhack'>@ironhack</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> ## Gradients Just like any normal drawing program, we can fill and stroke shapes using **linear** and **radial** gradients. We create a `CanvasGradient` object by using one of the following methods. :::info **`createLinearGradient(x1, y1, x2, y2)`** Creates a linear gradient object with a starting point of (x1, y1) and an end point of (x2, y2). **`createRadialGradient(x1, y1, r1, x2, y2, r2)`** Creates a radial gradient. The parameters represent two circles, one with its center at (x1, y1) and a radius of r1, and the other with its center at (x2, y2) with a radius of r2. ::: Once we've created a CanvasGradient object we can assign colors to it by using the `addColorStop()` method. :::info **`gradient.addColorStop(position, color)`** ::: Creates a new **color stop** on the gradient object. The position is a number between 0.0 and 1.0 and defines the relative position of the color in the gradient, and the color argument must be a string representing a `CSS <color>`, indicating the color the gradient should reach at that offset into the transition. ## Patterns Another option we have to give style to our elements is the `createPattern()` method. :::info **`createPattern(image, type)`** Creates and returns a new canvas pattern object. The *image* is a CanvasImageSource that is, an HTMLImageElement, another canvas, a `<video>` element, or the like. type is a string indicating how to use the image. ::: The type specifies how to use the image in order to create the pattern, and must be one of the following string values: - `repeat` - tiles the image in both vertical and horizontal directions. - `repeat-x` - tiles the image horizontally but not vertically. - `repeat-y` - tiles the image vertically but not horizontally. - `no-repeat` - doesn't tile the image. It's used only once. ## Text :::info lecture On va également pouvoir ajouter du texte à notre dessin : ::: We may need to add some text when we are using `canvas`, but don´t worry, there is a method exclusively for that purpose. When using `canvas` we have two option to render text. We will check both of them here: :::info <b>**`fillText(text, x, y, maxWidth)`**</b> Fills a given text at the given (x,y) position. Optionally with a maximum width to draw. **`strokeText(text, x, y , maxWidth)`** Strokes a given text at the given (x,y) position. Optionally with a maximum width to draw. ::: Here is a very simple example: <iframe height='265' scrolling='no' title='A fillText example' src='//codepen.io/ironhack/embed/dzgJWj/?height=265&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen <a href='https://codepen.io/ironhack/pen/dzgJWj/'>A fillText example</a> by Ironhack (<a href='https://codepen.io/ironhack'>@ironhack</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> ### Styling text In the examples above we are already making use of the font property to make the text a bit larger than the default size. There are some more properties which let you adjust the way the text gets displayed on the canvas: :::info **`font` = value** **The current text style being used when drawing text. This string uses the same syntax as the CSS font property. The default font is *10px sans-serif*. **`textAlign` = value** Text alignment setting. Possible values: *start*, *end*, *left*, *right* or *center*. The default value is *start*. **`textBaseline` = value** Baseline alignment setting. Possible values: **top**, **hanging**, **middle**, **alphabetic**, **ideographic**, **bottom**. The default value is **alphabetic**. **`direction` = value** Directionality. Possible values: *ltr*, *rtl*, *inherit*. The default value is *inherit*. ::: :::info lecture Ajoutons du texte a notre dessin `javascripts/intro.js` : ::: We can add some text to our previous example. Let's open our `canvas-basics` example and add the following code to the `intro.js` file: ```javascript // javascripts/intro.js // ... // **************************** // draw text // **************************** // color the text ctx.fillStyle = "orange"; ctx.font = "30px Arial"; // ctx.fillText("string", x, y); => x, y are coordinates where the text // is going to appear ctx.fillText("Hello there", 20, 40); ``` Finally, our example looks like this: ![](https://s3-eu-west-1.amazonaws.com/ih-materials/uploads/upload_42c737468e7c773ee9dff46fb3f70c81.jpeg =300x300) <div class="skip"> Before we move to the images, let's create a small example and add a bit dynamic to our project. We can reuse the *canvas-basics* example: ```bash $ cd canvas-basics touch index.html javascripts/index.js ``` And let's add some basic *html* into our `index.html` file: ```htmlmixed <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Canvas Basics - Example 2</title> <link rel="stylesheet" href="styles/style.css"> <script src="javascripts/index.js"></script> </head> <body> <!-- the magic happens inside this tag - it's HTML5 tag that supports animation --> <canvas id="example" width="300" height="300"></canvas> <button onclick="draw(0,0)">DRAW</button> </body> </html> ``` As we can see, we added a button `DRAW` so now we have to define what will happen when we click this button. Let's first get the *canvas* tag, define the *context*, and then let's create the rectangle. ```javascript // javascripts/index.js function draw(x, y){ // use id "example" to get <canvas></canvas> tag const theCanvas = document.getElementById("example"); // capture 2d context where everything happens in canvas // context has all the methods for drawing things const ctx = theCanvas.getContext("2d"); // colors rectangle with this color ctx.fillStyle = "green"; // creates rectangle => ctx.fillRect(x, y, width, height); ctx.fillRect(x, 0, 50, 50); } ``` Great. At this moment, if we open `index.html` and click on the button `DRAW` we can see this in our browser: ![](https://s3-eu-west-1.amazonaws.com/ih-materials/uploads/upload_4329d4b3f26dbf153f5e68f63d782870.jpeg =300x300) Now we will add a bit dynamic. Let's make our green square move on the *x-axis*. One way to do this is by using `clearRect()` method in combination with adding some number to *x-axis* (bigger number, faster movement). Our `index.js` now looks like this: ```javascript // javascripts/index.js function draw(x, y){ // use id "example" to get <canvas></canvas> tag const theCanvas = document.getElementById("example"); // capture 2d context where everything happens in canvas // context has all the methods for drawing things const ctx = theCanvas.getContext("2d"); // clears whole canvas to simulate animation (==movement) of the rectangle ctx.clearRect(0, 0, 300, 300); // colors rectangle with this color ctx.fillStyle = "green"; // creates rectangle => ctx.fillRect(x, y, width, height); ctx.fillRect(x, 0, 50, 50); // changes position of X coordinate x += 3; // calls itself every 30ms setTimeout(`draw(${x}, ${y})`, 30); } ``` Not that complicated, right? Great, let's move to images now! </div> ## Images :::info lecture On peut également rajouter des images : ::: Until now we have created our own shapes and applied styles to them. One of the more exciting features of `<canvas>` is the ability to use **images**. These can be used to do dynamic photo compositing or as backdrops of graphs, for sprites in games, and so forth. External images can be used in any format supported by the browser, such as **PNG, GIF, or JPEG**. :bulb: You can even use the image produced by other canvas elements on the same page as the source! Importing images into a canvas is basically a two-step process: 1. Get a reference to an HTMLImageElement object or to another canvas element as a source. It is also possible to use images by providing a URL. 2. Draw the image on the canvas using the drawImage() function. Let's take a look at how to do this. The canvas API is able to use any of the following data types as an image source: - **HTMLImageElement**. These are images created using the `Image()` constructor, as well as any `<img>` element. - **SVGImageElement**. These are images embedded using the `<img>` element. - **HTMLVideoElement**. Using an HTML `<video>` element as your image source grabs the current frame from the video and uses it as an image. - **HTMLCanvasElement**. You can use another `<canvas>` element as your image source. All these sources are collectively referred to by the type `CanvasImageSource`. There are several ways to get images for use on canvas. ### Reference an Image :::info lecture On peut utiliser une image de la page. ::: We can obtain a reference to images on the same page as the canvas by using one of: 1. The `document.images` collection 2. The `document.getElementsByTagName()` method. If you know the ID of the specific image you wish to use, you can use document.getElementById() to retrieve that specific image ### Creating an image from scratch :::info lecture Mais plus souvent, on voudra la créer nous même en JS : ::: Another option is to create `new HTMLImageElement` objects in our script. To do this, you can use the convenient `Image()` constructor: ```javascript var img = new Image(); // Create new <img> element img.src = 'myImage.png'; // Set source path ``` When this script gets executed, the image starts loading. :::warning If you try to call `drawImage()` before the image has finished loading, it won't do anything (or, in older browsers, may even throw an exception). So you need to be sure to use the load event so you don't try this before the image has loaded ::: ### Drawing images :::info lecture Une fois que l'on dispose de notre image, on peut la tracer : ::: Once we have a reference to our source image object we can use the `drawImage()` method to render it to the canvas. For loading our image, we should call the method in the following way: :::info **`drawImage(image, x, y)`** Draws the CanvasImageSource specified by the image parameter at the coordinates (x, y). ::: ### Scaling the Images :::info lecture Bien sûr, en général, on la retaillera par la même occasion : ::: Sometime we will need to scale our images, to fit a specific size. For that purpose we have two extra parameters on the `drawImage()` method: :::info `drawImage(image, x, y, width, height)` This adds the width and height parameters, which indicate the size to which to scale the image when drawing it onto the canvas. ::: <iframe height="500" style="width: 100%;" scrolling="no" title="drawImage" src="https://codepen.io/abernier/embed/NWWdVMj?height=300&theme-id=0&default-tab=js,result" frameborder="no" allowtransparency="true" allowfullscreen="true"> See the Pen <a href='https://codepen.io/abernier/pen/NWWdVMj'>drawImage</a> by Antoine BERNIER (<a href='https://codepen.io/abernier'>@abernier</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> <div class="skip"> Now when we theoretically got familiar with how to use images with *canvas*, let's go back to our *canvas-basics* example and let's replace our green square with some image 🎯 First, let's create *images* folder inside *canvas-basics* and let's go and save some image to it (pick any image). ```bash $ cd canvas-example $ mkdir images // <== save any image into this folder ``` In our example, we saved an image and named it *fireball* so we will reference it through *images/fireball.png*. In our *index.js* we will comment out the two lines where we create and set the rectangle color, and we will add image-related code. Let's do that first and later let's explain it a bit: ```javascript // javascripts/index.js // create new image object const fireballImg = new Image(); // "src" has to point as the image is used in HTML file fireballImg.src = "./images/fireball.png"; // set the start position of our image let fireballX = 0; let fireballY = 0; function draw(x, y){ // use id "example" to get <canvas></canvas> tag const theCanvas = document.getElementById("example"); // capture 2d context where everything happens in canvas // context has all the methods for drawing things const ctx = theCanvas.getContext("2d"); // clears whole canvas to simulate animation (==movement) of the rectangle ctx.clearRect(0, 0, 300, 300); // ctx.drawImage(whichImage, x, y, width, height); ctx.drawImage(fireballImg, fireballX, fireballY, 50, 50); // // colors rectangle with this color // ctx.fillStyle = "green"; // // creates rectangle => ctx.fillRect(x, y, width, height); // ctx.fillRect(x, 0, 50, 50); // changes position of X coordinate // x += 3; fireballX += 3; // calls itself every 30ms setTimeout(`draw(${x}, ${y})`, 30); } ``` Now we replaced rectangle with flying fireball: ![](https://s3-eu-west-1.amazonaws.com/ih-materials/uploads/upload_a9fc3491fe5238f1fb5f1da5a2c44594.jpeg =300x250) As you can see, the first thing we did was to create `new Image()` which later gave us the chance to add `src` attribute to image object and attach the source of the image to it (**remember, you have to navigate to the image source as if you were in *index.html* file**). Also, we set the start position of our image to (0,0) coordinates. In the function, we replicated the process as we already had in the previous example: clear the canvas and redraw the image on the new position (*fireballX +=3*). </div> Let's take a look into another example: ### Example <iframe height='265' scrolling='no' title='Canvas Images' src='//codepen.io/ironhack/embed/MvPMNw/?height=265&theme-id=0&default-tab=result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen <a href='https://codepen.io/ironhack/pen/MvPMNw/'>Canvas Images</a> by Ironhack (<a href='https://codepen.io/ironhack'>@ironhack</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> ## Practice Time We already know how to add a figure such as rectangles, lines, circles, and giving them styles. In this learning, we also learn how to draw other types of elements like text and images. So, now is time to practice everything. In the last learning, we draw the WiFi icon, in this exercise we want to draw the full logo. ![](https://s3-eu-west-1.amazonaws.com/ih-materials/uploads/upload_972642d95160aa12f745e3095bd7b4d6.png) ## Summary We have learned about different ways to style our canvas and the elements we include in it. Besides, we saw how we can incorporate Text and Images, and apply different styles. When drawing in `canvas` this method would be really useful. ## Extra Resources - [W3Schools Images](https://www.w3schools.com/graphics/canvas_images.asp) - [drawImage() - MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage)