Введение в Canvas
Изучите элемент HTML <canvas>: система координат, 2D-контекст рендеринга, масштабирование для HiDPI, доступность и примеры рисования.
Элемент HTML <canvas> — это прямоугольная поверхность для рисования, которой вы полностью управляете из скрипта. Сам элемент является лишь пустым контейнером — он ничего не рисует самостоятельно. Вы используете JavaScript, чтобы отдавать команды рисования, наносящие пиксели на поверхность.
Эта страница знакомит с элементом <canvas>, его системой координат, 2D-контекстом рендеринга и наиболее распространёнными операциями рисования: фигуры, текст, градиенты, линии и изображения.
Что такое элемент <canvas>?
<canvas> предоставляет вам растровое изображение — сетку пикселей, в которую вы рисуете напрямую. После того как фигура нарисована, canvas не запоминает её как объект; это просто окрашенные пиксели. Это ключевое отличие от SVG, где каждая фигура остаётся узлом DOM, который можно по-отдельности перестилизовать или анимировать.
Этот компромисс определяет, когда стоит выбирать canvas:
- Выбирайте
<canvas>для попиксельного контроля, работы с тысячами объектов, быстрой покадровой анимации, игр, обработки изображений, диаграмм с большим объёмом данных или эффектов частиц. Поскольку это «непосредственный режим» (immediate-mode), перерисовка обходится дёшево. - Выбирайте SVG, когда вам нужны независимые от разрешения векторы, масштабируемые иконки, управляемое количество фигур, которые можно инспектировать, по которым можно кликать или которые можно анимировать через CSS/DOM.
- Выбирайте CSS для макета, переходов и эффектов на обычных HTML-элементах — не для произвольного рисования.
На одной HTML-странице можно разместить несколько элементов <canvas>, каждый со своим собственным контекстом.
Система координат
Canvas использует двумерную координатную сетку. Начало координат (0, 0) находится в левом верхнем углу. Ось x увеличивается вправо, а ось y увеличивается вниз — заметьте, что y растёт вниз, в отличие от математического графика. Точка (100, 60) находится на расстоянии 100 пикселей от левого края и 60 пикселей от верха.
Область рисования задаётся атрибутами width и height (в CSS-пикселях):
<canvas id="canvas" width="250" height="150"></canvas>Задавайте размер canvas с помощью атрибутов width и height, а не через CSS. CSS-свойства width/height лишь растягивают существующее растровое изображение, что размывает рисунок. Добавляйте рамку через атрибут style или класс.
2D-контекст рендеринга
Вы никогда не рисуете непосредственно на элементе <canvas> — вы рисуете через контекст рендеринга, объект, который предоставляет API рисования. Вы получаете его с помощью getContext():
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');getContext('2d') возвращает CanvasRenderingContext2D, который содержит все методы и свойства, используемые ниже (fillRect, arc, fillText, strokeStyle и т. д.). Это правильная отправная точка практически для любого 2D-рисования.
Существуют и другие типы контекстов для различных нужд:
'webgl'/'webgl2'— ускоренные GPU трёхмерная (и высокопроизводительная двумерная) графика через API OpenGL ES.'bitmaprenderer'— отображаетImageBitmapбез собственного API рисования.
В этой главе используется только контекст '2d'.
Доступность: резервный контент
Всё, что вы размещаете между открывающим и закрывающим тегами <canvas>, является резервным контентом. Браузеры, поддерживающие canvas, игнорируют его; браузеры (или вспомогательные технологии), которые не могут отобразить canvas, показывают его вместо него. Поскольку нарисованные пиксели невидимы для программ чтения с экрана, используйте это пространство для описания графики или добавьте aria-label / role, чтобы canvas был осмысленно озвучен.
<canvas id="canvas" width="250" height="150" role="img" aria-label="A blue circle on a white background">
A blue circle on a white background.
</canvas>Для интерактивных canvas-элементов (игры, приложения для рисования) предоставляйте в качестве резервного варианта реальные фокусируемые HTML-элементы управления внутри элемента, поскольку на сами пиксели нельзя перейти с помощью клавиши Tab.
Пример тега HTML <canvas>:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" style="border:1px solid #1c87c9;">
The HTML5 canvas tag is not supported by your browser.
</canvas>
</body>
</html>Пример тега HTML <canvas> для рисования окружности:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="exampleCanvas" width="200" height="200" style="border:1px solid #dddddd;">
HTML5 canvas tag is not supported by your browser.
</canvas>
<script>
const c = document.getElementById("exampleCanvas");
const ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(100, 100, 60, 0, 2 * Math.PI);
ctx.strokeStyle = '#009299';
ctx.stroke();
</script>
</body>
</html>Метод arc() принимает пять аргументов: arc(x, y, radius, startAngle, endAngle). Здесь (100, 100) — центр окружности, 60 — радиус в пикселях, а дуга проходит от угла 0 до 2 * Math.PI. Углы измеряются в радианах, и полная окружность составляет 2π радиан, поэтому от 0 до 2 * Math.PI рисуется полная окружность. beginPath() начинает новый путь, а stroke() обводит его, используя текущий strokeStyle. Чтобы залить окружность вместо обводки, задайте fillStyle и вызовите fill(). Подробнее — в Рисование в Canvas и Координаты Canvas.
Пример тега HTML <canvas> для рисования текста:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="exampleCanvas" width="350" height="110" style="border:1px solid #dddddd;">
HTML5 canvas tag is not supported by your browser.
</canvas>
<script>
const c = document.getElementById("exampleCanvas");
const ctx = c.getContext("2d");
ctx.font = "40px Arial";
ctx.fillStyle = '#262ac7';
ctx.fillText("Canvas Text", 55, 65);
</script>
</body>
</html>fillText(text, x, y) рисует закрашенный текст по заданным координатам. Свойство font использует стандартное CSS-сокращение для шрифтов. Смотрите Текст в Canvas для информации о выравнивании, обводке текста и измерении ширины.
Пример тега HTML <canvas> для рисования линейного градиента:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="exampleCanvas" width="300" height="140" style="border:1px solid #dddddd;">
The HTML5 canvas tag is not supported by your browser.
</canvas>
<script>
const c = document.getElementById("exampleCanvas");
const ctx = c.getContext("2d");
const grd = ctx.createLinearGradient(0, 0, 300, 0);
grd.addColorStop(0, "#359900");
grd.addColorStop(1, "#ffffff");
ctx.fillStyle = grd;
ctx.fillRect(20, 20, 250, 100);
</script>
</body>
</html>createLinearGradient(x0, y0, x1, y1) задаёт направление градиента двумя точками. Здесь от (0, 0) до (300, 0) — горизонтальный градиент слева направо. addColorStop(offset, color) помещает цвет на позицию от 0 (начало) до 1 (конец), поэтому зелёный плавно переходит в белый. Присвоение градиента fillStyle заставляет fillRect(x, y, width, height) закрашивать им. Подробнее — в Градиенты Canvas.
Пример тега HTML <canvas> для рисования линии:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="exampleCanvas" width="150" height="150" style="border:1px solid #cccccc;">
The HTML5 canvas tag is not supported by your browser.
</canvas>
<script>
const c = document.getElementById("exampleCanvas");
const ctx = c.getContext("2d");
ctx.moveTo(0, 0);
ctx.lineTo(150, 150);
ctx.strokeStyle = '#86417d';
ctx.stroke();
</script>
</body>
</html>moveTo(x, y) поднимает «перо» в начальную точку без рисования, а lineTo(x, y) добавляет прямой отрезок до этой точки. Ничего не появится, пока вы не вызовете stroke(). Смотрите Рисование в Canvas для информации о многосегментных путях, ширине линии и соединениях.
Пример тега HTML <canvas> для рисования изображения:
Чтобы оставаться самодостаточным примером (и избежать проблем с CORS — см. примечание ниже), этот пример использует небольшое встроенное SVG-изображение в виде data URI вместо удалённой фотографии:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<h2>Draw an image with canvas</h2>
<canvas id="exampleCanvas" width="220" height="120" style="border:1px solid #dddddd;"></canvas>
<script>
const canvas = document.getElementById('exampleCanvas');
const ctx = canvas.getContext('2d');
const image = new Image();
image.addEventListener('load', () => {
// drawImage(image, dx, dy) draws at the given top-left position
ctx.drawImage(image, 10, 10);
// Scaled copy: drawImage(image, dx, dy, dWidth, dHeight)
ctx.drawImage(image, 120, 10, 50, 50);
});
image.src =
"data:image/svg+xml," +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">' +
'<circle cx="50" cy="50" r="45" fill="#1c87c9" /></svg>'
);
</script>
</body>
</html>drawImage() принимает три формы: drawImage(image, dx, dy), drawImage(image, dx, dy, dWidth, dHeight) для масштабирования и drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) для вырезания прямоугольника из источника (s*) и помещения его в прямоугольник назначения (d*). Всегда рисуйте внутри обработчика события load изображения, чтобы пиксели были готовы. Подробнее — в Изображения в Canvas.
Ловушка CORS. Рисование изображения из другого источника без соответствующих заголовков CORS «заражает» canvas. После этого getImageData() и toDataURL() выбрасывают ошибку безопасности. Если вам нужно считать пиксели из удалённого изображения, оно должно раздаваться с разрешающими заголовками CORS, а перед установкой src необходимо задать image.crossOrigin = "anonymous".
Пример тега HTML <canvas> для рисования радиального градиента:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="exampleCanvas" width="260" height="160" style="border:1px solid #cdcdcd;">
The HTML5 canvas tag is not supported by your browser.
</canvas>
<script>
const c = document.getElementById("exampleCanvas");
const ctx = c.getContext("2d");
const grd = ctx.createRadialGradient(150, 75, 10, 115, 90, 150);
grd.addColorStop(0, "purple");
grd.addColorStop(1, "white");
ctx.fillStyle = grd;
ctx.fillRect(20, 20, 220, 120);
</script>
</body>
</html>createRadialGradient(x0, y0, r0, x1, y1, r1) создаёт переход между двумя окружностями: начальной (центр (150, 75), радиус 10) и конечной (центр (115, 90), радиус 150). Цветовые стопы плавно переходят от фиолетового у внутренней окружности к белому у внешней, создавая круглое свечение. Сравните с createLinearGradient выше и читайте подробнее в Градиенты Canvas.
Экраны с высоким DPI (Retina)
На экранах с высокой плотностью пикселей один CSS-пиксель соответствует нескольким аппаратным пикселям. Canvas, размер которого задан только в CSS-пикселях, будет размытым на таких экранах. Решение заключается в масштабировании растрового изображения на window.devicePixelRatio с последующим масштабированием контекста, чтобы координаты рисования остались прежними:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const ratio = window.devicePixelRatio || 1;
// CSS size (layout) stays the same:
const cssWidth = 250;
const cssHeight = 150;
canvas.style.width = cssWidth + 'px';
canvas.style.height = cssHeight + 'px';
// Backing bitmap gets more device pixels:
canvas.width = cssWidth * ratio;
canvas.height = cssHeight * ratio;
// Scale once so you keep drawing in CSS-pixel coordinates:
ctx.scale(ratio, ratio);После этого рисование arc(100, 100, 60, …) создаёт чёткую окружность как на стандартных, так и на Retina-экранах.
Связанные главы
- Рисование в Canvas — пути, прямоугольники, заливки и обводки
- Текст в Canvas — шрифты, выравнивание и измерение текста
- Координаты Canvas — сетка, перемещение и вращение
- Градиенты Canvas — линейные и радиальные цветовые переходы
- Изображения в Canvas — рисование и обработка растровых изображений
- Справочник Canvas — полный список методов и свойств контекста