Canvas Images
Learn how to draw images on an HTML canvas: get an image source, use the three forms of drawImage(), handle CORS, and avoid a tainted canvas.
One of the features of the <canvas> element is the possibility of using images. These can be used for different purposes. You can use external images in any format supported by the browser (e.g., PNG, GIF, or JPEG). As a source, you can also use the image created by other canvas elements.
This chapter builds on the Canvas introduction and Canvas drawing basics. Here you will learn how to get an image source, how to use the three forms of the drawImage() method, and how to avoid the "tainted canvas" problem when loading images from other domains.
Importing images into a canvas is a two-step process:
- Get a reference to another canvas element or an HTMLImageElement object as a source.
- Draw an image on the canvas with the drawImage() function.
As an image source, the canvas API can use any of the following data types:
| Data Type | Description |
|---|---|
| HTMLImageElement | These are images created with the Image() constructor, or any <img> element. |
| SVGImageElement | These are images embedded with the <image> element. |
| HTMLVideoElement | An HTML <video> element as an image source takes the current frame from the video and uses it as an image. |
| HTMLCanvasElement | As an image source, you can use another <canvas> element. |
These sources together are referred to by the type CanvasImageSource.
There are many ways to get images for use on canvas.
Using images from the same page
You can get a reference to images on the same page with the canvas with one of the following:
- The
document.imagescollection - The
document.getElementsByTagName()method - The
document.getElementById()method if you know the ID of the image you want to use
For example, to grab an existing <img> element by its ID and draw it once the page has loaded:
<img id="photo" src="myImage.png" alt="My image" />
<canvas id="exampleCanvas" width="240" height="225"></canvas>
<script>
window.onload = function () {
const canvas = document.getElementById("exampleCanvas");
const ctx = canvas.getContext("2d");
const img = document.getElementById("photo");
ctx.drawImage(img, 0, 0);
};
</script>Because the <img> is already in the document, the browser has usually finished loading it by the time window.onload fires, so it is safe to draw immediately.
Using images from other domains
Using the <img> element with the crossorigin="anonymous" attribute, you can request permission to load an image from another domain. If the hosting domain allows cross-domain access via CORS headers, you can use the image in your canvas without tainting it.
<img id="remote" src="https://example.com/photo.jpg" crossorigin="anonymous" alt="Remote image" />When you set crossorigin="anonymous", the browser sends the request without credentials (cookies). The image is only allowed onto a clean canvas if the server responds with the header:
Access-Control-Allow-Origin: *(or a value that includes your origin). If you create the image in JavaScript, set the property before the src:
const img = new Image();
img.crossOrigin = "anonymous";
img.onload = function () {
ctx.drawImage(img, 0, 0);
};
img.src = "https://example.com/photo.jpg";Note If you draw a cross-origin image without proper CORS setup, the canvas becomes "tainted". A tainted canvas blocks access to its pixel data for security reasons — calling
toDataURL(),toBlob(), orgetImageData()will throw aSecurityError. The fix is on the server: it must send theAccess-Control-Allow-Originheader. There is no way to read pixels from a tainted canvas on the client side.
Using other canvas elements
Other canvas elements can be accessed using either the document.getElementById() or document.getElementsByTagName() method.
Embedding an image via data: URL
Data URLs allow specifying an image as a Base64 encoded string of characters directly in the code. The data URL’s advantage is that the resulting image is available immediately. It is also possible to wrap all your CSS, HTML, JavaScript, and images in one file.
However, there is also a disadvantage: the image is not cached, and the encoded URL can be too long for larger images.
To use a data URL, assign it to the src of an image and draw it once it has loaded:
const img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0);
};
img.src =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==";The string after base64, is the encoded image. Because it is part of the script, no extra network request is made — the image is ready as soon as it is decoded.
Creating an image from scratch
You can also create a new HTMLImageElement object in your script. For this, use the Image() constructor:
Creating an image from scratch
const img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0);
};
img.src = "myImage.png";The image begins loading the moment src is set, and loading is asynchronous. If you call drawImage() before the image has finished loading, nothing is drawn. To make sure drawImage() runs only after the image is ready, attach the onload event handler before setting src.
The drawImage() function
Once a reference to the source image is available, you can use the drawImage() method. It comes in three forms, each accepting more parameters than the last:
1. Position only
ctx.drawImage(image, dx, dy);Draws the whole image at its natural size, placing its top-left corner at the point (dx, dy) on the canvas.
2. Position and size (scaling)
ctx.drawImage(image, dx, dy, dWidth, dHeight);Draws the whole image, scaled to fit a box dWidth × dHeight pixels whose top-left corner is at (dx, dy).
3. Slicing (source rectangle to destination rectangle)
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);Takes a rectangular slice of the source image and draws it into a destination rectangle on the canvas. This is the most flexible form — it is used for sprite sheets, cropping, and zooming.
The parameters split into two groups:
| Parameter | Group | Meaning |
|---|---|---|
sx, sy | Source | Top-left corner of the slice cut from the source image. |
sWidth, sHeight | Source | Width and height of that slice. |
dx, dy | Destination | Where the slice is placed on the canvas. |
dWidth, dHeight | Destination | Size the slice is drawn at on the canvas (it is scaled if different from sWidth/sHeight). |
The s* (source) values are measured in the original image's pixels; the d* (destination) values are measured in canvas pixels. The basic form, drawImage(image, dx, dy), is just the first of these three.
In the following example, we use the document.getElementById() method to get a reference to the image and then use the drawImage() function to draw it.
Example of drawing an image with the drawImage() function:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<h2>Image</h2>
<img id="photo" src="/uploads/media/default/0001/01/25acddb3da54207bc6beb5838f65f022feaa81d7.jpeg" alt="Aleq" width="200" height="185" />
<h2>Image with canvas:</h2>
<canvas id="exampleCanvas" width="240" height="225" style="border:2px solid #cccccc;">
Your browser doesn't support the canvas tag.
</canvas>
<script>
window.onload = function() {
const canvas = document.getElementById("exampleCanvas");
const ctx = canvas.getContext("2d");
const img = document.getElementById("photo");
ctx.drawImage(img, 20, 20);
};
</script>
</body>
</html>SVG images must define the width and height in the root <svg> element.
Using frames from a video
It is also possible to use frames from a video that is presented by a <video> element, even when the video is not visible. E.g., if you have a <video> element with the ID "videoCanvas", do the following:
Example of drawing a video with canvas:
Canvas Images
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<h2>Video</h2>
<video id="videoCanvas" controls width="350" autoplay>
<source src="/build/videos/arcnet.io(7-sec).mp4" type="video/mp4" />
</video>
<h2>Canvas draws the current video:</h2>
<canvas id="exampleCanvas" width="310" height="190" style="border:1px solid #d3d3d3;">
Your browser doesn't support the canvas tag.
</canvas>
<script>
const v = document.getElementById("videoCanvas");
const c = document.getElementById("exampleCanvas");
const ctx = c.getContext("2d");
let animId;
function drawFrame() {
ctx.drawImage(v, 5, 5, 300, 180);
animId = requestAnimationFrame(drawFrame);
}
v.addEventListener("play", function() {
animId = requestAnimationFrame(drawFrame);
}, false);
v.addEventListener("pause", function() {
cancelAnimationFrame(animId);
}, false);
v.addEventListener("ended", function() {
cancelAnimationFrame(animId);
}, false);
</script>
</body>
</html>