Lesson 13 - Images and drawing on canvas using JavaScript
In the previous exercise, Solved tasks for JavaScript lessons 8-12, we've practiced our knowledge from previous lessons.
Welcome to another lesson about creating web applications in JavaScript. In the previous lesson, Solved tasks for JavaScript lessons 8-12, we finished a table editor. In today's JavaScript tutorial, we're going to work with images.
There're two types of images on the web - static and dynamic. You already
know static images and we can hardly do any magic with them. They're just
ordinary images (the <img>
tag) with which we can perform
just a few basic operations. We mostly manipulate them only as DOM elements.
Dynamic images can be represented in an HTML document by 2 tags -
<svg>
and <canvas>
. Although the first tag
is not static, we won't be able to do much with it yet. The
<canvas>
tag is a bit more interesting and the only one whose
content can't be defined by any other way than by JavaScript. Let's take a look
through everything.
Static images
As I mentioned earlier in the introduction, this is a classic image, which in
HTML is defined by the <img>
tag. Of course, we can handle
different events like clicks in the same way we did it e.g. for buttons. In
JavaScript, we can create it either classically as a DOM element:
let img = document.createElement("img");
Or, we can create a new Image
object, which is a shorthand for
creating the DOM element:
let img = new Image();
Loading images
Loading images is a big problem (as well as loading anything in JavaScript).
Before we start working with an image, it must be loaded. If we have an image in
HTML and we execute the JavaScript code in the load
event handler,
there's no problem. The browser will ensure that the handler will be triggered
not before everything (even the image) is loaded.
If we create images later in JavaScript, we have to wait till they're loaded.
The load
event of the image element will become handy:
let img = new Image(); img.src = "images/image1.jpg"; img.onload = function () { // here we can be sure the image is loaded }
Example: A switch
Let's create a switch. You know the ON-OFF switches for sure. Download the
images below and keep their names switch0.png
and
switch1.png
.
We'll create a page where will be a switch image with the switch
id
and with the switch0.png
file loaded as
default.
<img src="switch0.png" id="switch" alt="Switch" />
We'll create a script where we declare a mySwitch
variable
(since switch
is a reserved keyword) and, after the page is loaded,
we'll store our switch image element in it. As next, we'll create a
switchIt()
function and set it as an event handler for clicking the
image.
let mySwitch; function switchIt() { } window.onload = function () { mySwitch = document.getElementById("switch"); mySwitch.onclick = switchIt; }
Img.src vs. img.getAttribute("src")
Now it's just about the condition and changing src
, but... The
image object (in OOP, you'll discover it's called HTMLImageElement
)
has the src
property. Therefore, it's possible to change either the
value of the src
HTML attribute of the <img>
element or to assign the value directly to the src
property of the
JavaScript element. Unfortunately, these two possibilities behave a bit
differently in some browsers. While the getAttribute()
method
returns exactly what is written in the attribute, the src
property
returns the absolute path. So, if we'll compare the sources, we'll mostly do it
with the value in the attribute. To try it out, we'll change the image source
using the src
property.
function switchIt() { if (mySwitch .getAttribute("src") == "switch0.png") { mySwitch .src = "switch1.png"; } else { mySwitch .src = "switch0.png"; } }
Open and test the page, the switch should work.
Notice that we don't have to worry when the image loads, we're not interested in it at all. The user will wait anyway, but we're relieved from this as long as we don't work with the image any further.
Now, let's proceed to dynamic images.
Dynamic images
Dynamic images are drawn on canvas, which is represented by the
<canvas>
element. This element MUST have the
width
and height
attributes set. If we don't specify
them or set them in CSS, some browsers will not understand and the result will
be blurred. However, we can't draw directly on the <canvas>
element, we must get its context first. Maybe you're wondering what's it good
for, getting the context? It's a difference when we draw a 3D game and a 2D tree
on a canvas. 3D is currently being adopted as the WebGL standard and we won't
deal with it now. We'll be interested in the 2D context only.
Getting the context
We'll get the context of a canvas using the getContext()
method,
passing the context type as its string parameter. In our case, the context type
is "2d"
.
let canvas = document.getElementById("canvas"); let context = canvas.getContext("2d");
On this context we can call various method which draw on the canvas.
Rectangles
The basic object is a rectangle. We have two basic methods for drawing a
rectangle. These are fillRect()
and strokeRect()
.
They both have the same parameters which are x, y, width, and height. The
fillRect()
method will fill the rectangle, the
strokeRect()
will draw just its outline.
HTML part
<canvas width="320" height="200" id="canvas"></canvas>
JS part
let canvas = document.getElementById("canvas"); let context = canvas.getContext("2d"); context.fillRect(50, 50, 100, 100); context.strokeRect(200, 50, 100, 100);
Lines
We use paths to draw lines on a canvas. We must (or should) begin the paths
and then close them as well. We use the beginPath()
and
closePath()
to do so. We'll draw the line itself using the
lineTo(x, y)
method. The line will be drawn from the last cursor
position (which is [0;0] at the start). We can set this position by the
moveTo(x, y)
method. The method doesn't draw anything, just moves
the canvas cursor to a given position. We'll draw the path either by the
fill()
method, which fills the inside of the path with color (so
you can draw your own shapes, that's why you need to close the path), or the
stroke()
method that only draws the outline of the path. A simple
example below draws a line from the point [20;20] to [40;150].
context.beginPath(); context.moveTo(20, 20); context.lineTo(40, 150); context.closePath(); context.stroke();
The results:
Circles and arcs
The other things we can draw on the canvas are circles and arcs (circle
slices). This is all provided by the arc()
method which syntax
follows:
context.arc(x, y, radius, startAngle, endAngle, direction);
The x
and` y` arguments again specify the absolute position, in
this case the center of the circle. The radius
argument specifies
the radius of the circle, startAngle
is the angle from which the
circle will be drawn. We specify the angles in radians. From mathematics, we
should remember that the full angle is 2 * PI
and that degrees are
converted to radians as (PI / 180) * degrees
. The last argument is
direction
. It's a logical value
(true
/false
) which indicates whether the circle will
be drawn clockwise or counterclockwise. The default value is true
,
which indicates the clockwise direction. We've to use this method inside of a
path.
We draw a circle as follows:
context.beginPath(); context.arc(100, 100, 80, 0, Math.PI * 2); context.fill();
The reward for the code above will be this circle:
Styles
To make our drawings look good, we'll learn to use styles. We distinguish
styles to fill and stroke. Styles can be applied to all objects from rectangles
to circles. We have two basic properties that the stroke()
and
fill()
methods work with. These are fillStyle
and
strokeStyle
. The values of these properties are colors. We can
specify them using the usual CSS hexadecimal syntax like #ffffff
or
rgb(255, 255, 255)
. We can also use
rgba(255, 255, 255, 0.5)
, where the last value is the alpha channel
(opacity), or hsl
and hsla
(as in CSS 3). And the last
option is to specify the colors by their names if they have it (e.g.
"green"
).
Let's style some simple objects:
// We define styles before drawing the objects (calling the fill(), stroke(), or other drawing methods such as fillRect(), strokeRect()...) context.fillStyle = "#a8c101"; context.fillRect(10, 10, 50, 50);
External images
On the canvas, of course, we can also draw existing images, e.g. from files.
The images have to be already loaded, otherwise they won't be drawn. We draw an
image simply using the drawImage()
method. It accepts the image as
the first parameter, the X and Y positions of where to draw the image as the
second and third parameters. There are also methods with parameters for
scaling/reducing and cropping the image.
Image:
The result:
Text
In addition to all the shapes, text can also be rendered on a canvas. This
can be used, for example, as a watermark on photos in your gallery, labels of a
chart, or completely differently, just as your imagination allows. The basic
function is fillText(text, x, y)
. The text
here
represents the string we want to draw. The x
and` y` arguments are
the absolute position. We can render a simple text as follows:
context.fillText("ICT.social", 50, 50);
However, the text will be unstyled and quite small. Therefore, there's the
context.font
property that we need to set before the text is
rendered (smilarly as fillStyle
). The values of the property are
the same as the values of the CSS font
property. Let's set the text
size to 30 pixels, for example, and use the sans-serif
font.
context.font = "30px sans-serif"; context.fillText("ICT.social", 50, 50);
The result:
That would be enough for the canvas introduction. In the next lesson, 2D canvas context in JavaScript, we'll look at the canvas in more detail.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 13x (110.24 kB)
Application includes source codes in language JavaScript