Изображения на Canvas
Как рисовать изображения на HTML canvas: источники, три формы drawImage(), работа с CORS и защита от «заражённого» canvas.
Одной из возможностей элемента <canvas> является работа с изображениями. Их можно использовать в самых разных целях. Поддерживаются внешние изображения в любом формате, который поддерживает браузер (например, PNG, GIF или JPEG). В качестве источника также можно использовать изображения, созданные другими элементами canvas.
Эта глава опирается на основы из разделов Введение в Canvas и Рисование на Canvas. Здесь вы узнаете, как получить источник изображения, как использовать три формы метода drawImage(), а также как избежать проблемы «заражённого canvas» при загрузке изображений с других доменов.
Импорт изображений на canvas — это двухшаговый процесс:
- Получить ссылку на другой элемент canvas или объект HTMLImageElement в качестве источника.
- Нарисовать изображение на canvas с помощью функции drawImage().
В качестве источника изображения Canvas API может использовать любой из следующих типов данных:
| Тип данных | Описание |
|---|---|
| HTMLImageElement | Изображения, созданные конструктором Image() или любым элементом <img>. |
| SVGImageElement | Изображения, встроенные с помощью элемента <image>. |
| HTMLVideoElement | Элемент HTML <video> в качестве источника изображения берёт текущий кадр видео и использует его как изображение. |
| HTMLCanvasElement | В качестве источника изображения можно использовать другой элемент <canvas>. |
Все эти источники объединены под типом CanvasImageSource.
Существует множество способов получить изображения для использования на canvas.
Использование изображений с той же страницы
Получить ссылку на изображения, находящиеся на той же странице, что и canvas, можно одним из следующих способов:
- Коллекция
document.images - Метод
document.getElementsByTagName() - Метод
document.getElementById(), если вам известен ID нужного изображения
Например, чтобы получить существующий элемент <img> по его ID и нарисовать его после загрузки страницы:
<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>Поскольку элемент <img> уже находится в документе, браузер, как правило, успевает загрузить его к моменту срабатывания window.onload, поэтому рисовать можно сразу.
Использование изображений с других доменов
С помощью элемента <img> с атрибутом crossorigin="anonymous" можно запросить разрешение на загрузку изображения с другого домена. Если принимающий домен разрешает кросс-доменный доступ через заголовки CORS, изображение можно использовать на canvas без его «заражения».
<img id="remote" src="https://example.com/photo.jpg" crossorigin="anonymous" alt="Remote image" />При установке crossorigin="anonymous" браузер отправляет запрос без учётных данных (cookies). Изображение допускается на чистый canvas только в том случае, если сервер отвечает с заголовком:
Access-Control-Allow-Origin: *(или значением, которое включает ваш origin). Если изображение создаётся в JavaScript, свойство нужно установить до src:
const img = new Image();
img.crossOrigin = "anonymous";
img.onload = function () {
ctx.drawImage(img, 0, 0);
};
img.src = "https://example.com/photo.jpg";Note Если нарисовать кросс-доменное изображение без надлежащей настройки CORS, canvas становится «заражённым». Заражённый canvas блокирует доступ к пиксельным данным из соображений безопасности — вызовы
toDataURL(),toBlob()илиgetImageData()выбросятSecurityError. Решение проблемы — на стороне сервера: он должен отправлять заголовокAccess-Control-Allow-Origin. Читать пиксели из заражённого canvas на стороне клиента невозможно.
Использование других элементов canvas
К другим элементам canvas можно обратиться с помощью метода document.getElementById() или document.getElementsByTagName().
Встраивание изображения через data: URL
Data URL позволяют задать изображение в виде строки символов, закодированной в Base64, прямо в коде. Преимущество data URL состоит в том, что результирующее изображение доступно немедленно. Также появляется возможность объединить весь CSS, HTML, JavaScript и изображения в одном файле.
Однако есть и недостаток: изображение не кэшируется, а закодированный URL может быть слишком длинным для больших изображений.
Чтобы использовать data URL, присвойте его атрибуту src изображения и нарисуйте его после загрузки:
const img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0);
};
img.src =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==";Строка после base64, является закодированным изображением. Поскольку она является частью скрипта, дополнительный сетевой запрос не выполняется — изображение готово сразу после декодирования.
Создание изображения с нуля
В скрипте также можно создать новый объект HTMLImageElement. Для этого используется конструктор Image():
Создание изображения с нуля
const img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0);
};
img.src = "myImage.png";Изображение начинает загружаться в момент присвоения src, и загрузка происходит асинхронно. Если вызвать drawImage() до завершения загрузки изображения, ничего нарисовано не будет. Чтобы гарантировать выполнение drawImage() только после готовности изображения, обработчик события onload нужно прикрепить до установки src.
Функция drawImage()
После получения ссылки на исходное изображение можно использовать метод drawImage(). Он существует в трёх формах, каждая из которых принимает больше параметров, чем предыдущая:
1. Только позиция
ctx.drawImage(image, dx, dy);Рисует всё изображение в его естественном размере, помещая левый верхний угол в точку (dx, dy) на canvas.
2. Позиция и размер (масштабирование)
ctx.drawImage(image, dx, dy, dWidth, dHeight);Рисует всё изображение, масштабируя его до прямоугольника dWidth × dHeight пикселей с левым верхним углом в точке (dx, dy).
3. Вырезание (прямоугольник источника в прямоугольник назначения)
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);Вырезает прямоугольный фрагмент из исходного изображения и рисует его в целевом прямоугольнике на canvas. Это наиболее гибкая форма — она используется для спрайтов, обрезки и масштабирования.
Параметры делятся на две группы:
| Параметр | Группа | Значение |
|---|---|---|
sx, sy | Источник | Левый верхний угол фрагмента, вырезаемого из исходного изображения. |
sWidth, sHeight | Источник | Ширина и высота этого фрагмента. |
dx, dy | Назначение | Место размещения фрагмента на canvas. |
dWidth, dHeight | Назначение | Размер, в котором фрагмент рисуется на canvas (масштабируется, если отличается от sWidth/sHeight). |
Значения s* (источник) измеряются в пикселях исходного изображения; значения d* (назначение) — в пикселях canvas. Базовая форма drawImage(image, dx, dy) является первой из этих трёх.
В следующем примере метод document.getElementById() используется для получения ссылки на изображение, после чего функция drawImage() рисует его на canvas.
Пример рисования изображения с помощью функции drawImage():
<!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.
Использование кадров из видео
Также можно использовать кадры из видео, представленного элементом <video>, даже если видео не отображается на странице. Например, если у вас есть элемент <video> с ID "videoCanvas", выполните следующее:
Пример рисования видео с помощью 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>