Рисование на Canvas
Рисование на HTML canvas с помощью JavaScript: прямоугольники, контуры, линии, окружности и дуги — с примерами кода.
Элемент <canvas> — это просто чистая прозрачная поверхность для рисования. Сам по себе он ничего не отображает — нарисовать что-либо с помощью HTML или CSS на нём невозможно. Всё рисование на canvas выполняется с помощью JavaScript через объект, называемый контекстом рендеринга. На этой странице шаг за шагом рассматриваются основные операции 2D-рисования: заливка прямоугольников, обводка контуров, рисование произвольных путей, а также окружностей и дуг.
Если вы ещё не создавали элемент <canvas>, начните с изучения тега HTML <canvas> и введения в Canvas.
Система координат canvas
Все методы рисования используют пиксельные координаты. Сетка canvas имеет начало координат (0, 0) в верхнем левом углу. Ось x растёт вправо, а ось y растёт вниз (обратите внимание: y увеличивается при движении вниз, в отличие от математического графика). Таким образом, точка (0, 0) — это верхний левый пиксель, а (width, height) — нижний правый угол.
Подробнее о системе координат читайте в разделе Координаты canvas.
1. Получение элемента canvas
Сначала получите ссылку на элемент <canvas> в DOM с помощью getElementById():
const canvas = document.getElementById("canvas");2. Получение контекста 2D-рендеринга
Вызовите метод getContext(), чтобы получить контекст рисования. При передаче "2d" возвращается объект CanvasRenderingContext2D, который содержит все свойства и методы для рисования:
const ctx = canvas.getContext("2d");Переменная ctx (сокращение от «context») — это то, на чём вызываются все команды рисования.
getContext() возвращает null, если браузер не может предоставить запрошенный контекст (например, если "2d" написано с ошибкой или элемент не является <canvas>). Хорошей практикой является проверка этого до начала рисования, чтобы отсутствие контекста приводило к тихому завершению, а не к исключению на следующей строке:
const ctx = canvas.getContext("2d");
if (!ctx) return; // bail out if the 2D context is unavailable; only valid inside a functionВ обычном встроенном <script> (который не является телом функции) заверните рисование в if (ctx) { ... }, как показано в примерах ниже.
3. Рисование залитого прямоугольника
Свойство fillStyle задаёт цвет, используемый для заливки фигур. Допустимо любое значение цвета CSS, паттерн или градиент:
ctx.fillStyle = "#1c87c9";Затем fillRect(x, y, width, height) рисует залитый прямоугольник. Первые два параметра — координаты x и y верхнего левого угла, затем ширина и высота в пикселях:
ctx.fillRect(0, 0, 230, 130);Пример рисования залитого прямоугольника:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="A blue filled rectangle" style="border:1px solid #dddddd;">
Your browser does not support the canvas element.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
// only draw when the 2D context is available
ctx.fillStyle = "#1c87c9";
ctx.fillRect(0, 0, 230, 130);
}
</script>
</body>
</html>Обеспечение доступности canvas
Всё нарисованное на canvas — это просто пиксели без какой-либо структуры, поэтому программы чтения с экрана не видят это содержимое. Нет ничего, что можно было бы объявить, а клавиатурные пользователи не могут добраться до элементов внутри. Предоставьте вспомогательным технологиям текстовый эквивалент двумя способами:
- Резервное содержимое размещается между тегами
<canvas>. Браузеры, поддерживающие canvas, игнорируют его; вспомогательные технологии (и очень старые браузеры) используют его вместо canvas. Добавьте здесь осмысленное описание, а не «ваш браузер не поддерживает canvas». role="img"вместе сaria-labelописывает готовый рисунок как единое изображение — так же, как атрибутaltописывает<img>.
<canvas id="chart" width="250" height="150" role="img"
aria-label="Bar chart: sales doubled from Q1 to Q2.">
A bar chart showing sales doubling from Q1 to Q2.
</canvas>Для интерактивных элементов (кликабельные области, элементы управления) предоставьте также реальные DOM-элементы с фокусировкой — canvas сам по себе недоступен с клавиатуры.
Обводка вместо заливки
Иногда нужен контур фигуры, а не сплошная заливка. Используйте свойство strokeStyle для задания цвета контура и strokeRect(x, y, width, height) для рисования незаполненного прямоугольника. Свойство lineWidth управляет толщиной контура:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="A blue rectangular outline" style="border:1px solid #dddddd;">
A rectangle drawn as an outline.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.strokeStyle = "#1c87c9";
ctx.lineWidth = 4;
ctx.strokeRect(20, 20, 200, 100);
}
</script>
</body>
</html>Очистка части canvas
clearRect(x, y, width, height) стирает заданный прямоугольник, делая его полностью прозрачным. Чаще всего используется для очистки всего canvas перед перерисовкой (например, на каждом кадре анимации):
// Erase everything on a 250 × 150 canvas
ctx.clearRect(0, 0, 250, 150);В следующем примере рисуются два синих прямоугольника, а затем clearRect() вырезает прозрачное отверстие в середине canvas — обратите внимание, что правый прямоугольник частично стёрт:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="Two blue squares with a cleared rectangle cut out of the middle"
style="border:1px solid #dddddd;">
Two filled squares with a cleared region.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.fillStyle = "#1c87c9";
ctx.fillRect(20, 20, 100, 100);
ctx.fillRect(130, 20, 100, 100);
ctx.clearRect(90, 50, 70, 50); // erase a rectangle across both
}
</script>
</body>
</html>Рисование путей (линии и произвольные фигуры)
Прямоугольники удобны, но большинство рисунков строится с использованием путей — последовательностей точек, соединённых линиями или кривыми. Путь формируется набором методов и не становится видимым до вызова stroke() (для рисования контура) или fill() (для заливки замкнутой области).
| Метод | Действие |
|---|---|
beginPath() | Начинает новый пустой путь. |
moveTo(x, y) | Перемещает «перо» в точку (x, y) без рисования. |
lineTo(x, y) | Добавляет прямую линию от текущей точки до (x, y). |
closePath() | Рисует линию обратно к начальной точке пути. |
stroke() | Отображает путь в виде контура. |
fill() | Заливает область, ограниченную путём. |
Всегда начинайте с beginPath()
beginPath() очищает список точек, которые контекст отслеживает в данный момент, и начинает новый путь. Это важно, потому что canvas запоминает каждый подпуть до сброса. Если нарисовать одну фигуру, а затем начать добавлять точки для второй без вызова beginPath(), старые точки остаются в пути — и следующий вызов stroke() или fill() перерисует первую фигуру тоже, часто с новым цветом и шириной линии. В результате фигуры «перетекают» друг в друга.
Правило большого пальца: вызывайте beginPath() перед каждой новой фигурой. Каждый пример ниже начинается именно так.
Пример: одна линия
Чтобы нарисовать линию, начните путь, переместитесь в начальную точку, проведите линию до конечной точки, а затем выполните обводку:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="A diagonal blue line" style="border:1px solid #dddddd;">
A diagonal line drawn across the canvas.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.beginPath();
ctx.moveTo(20, 20); // start point (x, y)
ctx.lineTo(220, 120); // end point (x, y)
ctx.lineWidth = 3;
ctx.strokeStyle = "#1c87c9";
ctx.stroke(); // make the line visible
}
</script>
</body>
</html>Пример: треугольник
Последовательные вызовы lineTo() создают многоугольную фигуру. closePath() соединяет последнюю точку с первой, а fill() заливает замкнутую область:
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="A filled green triangle" style="border:1px solid #dddddd;">
A filled green triangle.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.beginPath();
ctx.moveTo(125, 20); // top vertex
ctx.lineTo(220, 130); // bottom-right vertex
ctx.lineTo(30, 130); // bottom-left vertex
ctx.closePath(); // back to the top vertex
ctx.fillStyle = "#8ebf42";
ctx.fill();
}
</script>
</body>
</html>Рисование окружностей и дуг
Метод arc() рисует окружности и дуги:
ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise);x,y— координаты центра дуги.radius— радиус в пикселях.startAngle,endAngle— начальный и конечный углы в радианах (не в градусах). Угол0направлен вдоль положительной оси x — прямо вправо, в направлении 3 часов — и по умолчанию углы увеличиваются по часовой стрелке (потому что ось y canvas направлена вниз). Полная окружность равна2 * Math.PIрадиан.counterclockwise— необязательный boolean. Передайтеtrueдля построения дуги в обратном направлении (по умолчаниюfalse).
Поскольку углы задаются в радианах, удобно использовать небольшую вспомогательную функцию для перевода из градусов:
function toRadians(degrees) {
return degrees * Math.PI / 180;
}
// e.g. a quarter turn:
ctx.arc(125, 75, 50, 0, toRadians(90));Таким образом, 90 градусов — это Math.PI / 2, 180 градусов — Math.PI, а 360 градусов — 2 * Math.PI.
Полная окружность строится от 0 до 2 * Math.PI. Как и для любого пути, для отображения необходимо вызвать stroke() или fill():
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<canvas id="canvas" width="250" height="150" role="img"
aria-label="A blue filled circle" style="border:1px solid #dddddd;">
A filled blue circle.
</canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.beginPath();
ctx.arc(125, 75, 50, 0, 2 * Math.PI); // center (125,75), radius 50, full circle
ctx.fillStyle = "#1c87c9";
ctx.fill();
}
</script>
</body>
</html>Чтобы нарисовать полукруг (полуокружность), завершите дугу на Math.PI радиан вместо 2 * Math.PI.
Что изучить дальше
Освоив фигуры и пути, исследуйте остальные разделы руководства по Canvas:
- Координаты canvas — подробнее о системе координат.
- Текст на canvas — вывод текста с помощью
fillText()иstrokeText(). - Градиенты canvas — линейные и радиальные цветовые градиенты.
- Изображения на canvas — рисование изображений с помощью
drawImage(). - Справочник по canvas — полный список свойств и методов.