JavaScript Canvas API
Узнайте, как рисовать графику с помощью элемента canvas и JavaScript: контекст 2D, фигуры, линии, трансформации и анимация с requestAnimationFrame.
Введение в HTML Canvas с JavaScript
Элемент HTML <canvas> — это поверхность для рисования, которой вы управляете с помощью JavaScript. В отличие от изображений или SVG, canvas работает в режиме немедленного рисования: здесь нет объектов фигур, за которыми нужно следить. Вы отдаёте команды рисования, и пиксели закрашиваются немедленно. После закрашивания фигура превращается в просто окрашенные пиксели — canvas не помнит о ней, и именно поэтому перерисовка является ключевой идеей любой анимации.
В этой главе рассматриваются: получение 2D-контекста, рисование фигур и линий, преобразование системы координат и анимация с помощью requestAnimationFrame.
На этой странице описывается контекст рендеринга 2D (getContext('2d')). Canvas также предоставляет контекст webgl для аппаратно-ускоренной 3D-графики, который является отдельным API.
Понимание элемента 'canvas'
Элемент <canvas> создаёт поверхность для рисования фиксированного размера, которая отображает графику «на лету». Он хорошо подходит для задач с интенсивной графикой — игры, диаграммы, обработка изображений, эффекты частиц — там, где нужен контроль на уровне пикселей и высокая частота кадров. Сам по себе тег ничего не рисует; это пустой контейнер, и всё рисование выполняет JavaScript.
Важный момент, который стоит усвоить сразу: атрибуты width и height задают размер буфера рисования, тогда как CSS-свойства width/height задают отображаемый размер. При несовпадении canvas растягивается, и рисунки выглядят размыто или искажённо. Устанавливайте размер через атрибуты (или в JS через canvas.width/canvas.height), а не через CSS, если только вы намеренно не хотите масштабировать изображение.
Базовая настройка Canvas
Чтобы начать рисовать, разместите тег <canvas> в HTML, затем получите его 2D-контекст рисования в JavaScript с помощью getContext('2d'). Объект контекста содержит все методы рисования.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas Example</title>
<style>
canvas {
border: 1px solid #000;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
</script>
</body>
</html>Здесь мы определяем canvas размером 200×200, добавляем ему рамку, чтобы видеть его границы, и получаем контекст с помощью getContext('2d'). Рамка чисто визуальная; сама поверхность canvas изначально полностью прозрачна.
Обратите внимание на систему координат: начало (0, 0) находится в верхнем левом углу. Ось x направлена вправо, а ось y — вниз, то есть (0, 0) — это верхний край, а большие значения y смещаются к нижнему. Это сбивает с толку новичков, привыкших к математическим осям.
Рисование фигур
Самый быстрый способ что-то нарисовать — это fillRect(x, y, width, height), который рисует закрашенный прямоугольник за один вызов. Сначала установите fillStyle, чтобы выбрать цвет (подходит любая строка цвета CSS). Также существует strokeRect для прямоугольника только с контуром и clearRect для очистки области до прозрачной.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rectangle Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="rectangleCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('rectangleCanvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#FF0000'; // Set the fill color to red
ctx.fillRect(20, 20, 150, 100); // Draw the rectangle
</script>
</body>
</html>Создание линий и путей
Линии, кривые и произвольные фигуры строятся из путей. Порядок действий всегда одинаков:
beginPath()— начать новый путь (если пропустить, новая линия продолжит предыдущую).moveTo(x, y)— поднять «перо» и переместить его без рисования.lineTo(x, y)— нарисовать отрезок до новой точки (вызывайте несколько раз для цепочки отрезков).stroke()— нарисовать контур, илиfill()— заполнить замкнутую область.
Управляйте внешним видом линии с помощью lineWidth, strokeStyle и lineCap перед вызовом stroke().
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Line Drawing Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="lineCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('lineCanvas');
const ctx = canvas.getContext('2d');
ctx.beginPath(); // Start the path
ctx.moveTo(50, 50); // Move the pen to (50, 50)
ctx.lineTo(150, 50); // Draw a line to (150, 50)
ctx.lineTo(150, 150); // Continue to (150, 150)
ctx.lineWidth = 4; // Thicker line
ctx.strokeStyle = 'navy'; // Line color
ctx.stroke(); // Render the path visible
</script>
</body>
</html>Рисование окружностей и дуг
Окружности рисуются с помощью arc(x, y, radius, startAngle, endAngle), где углы задаются в радианах (полная окружность — это 2 * Math.PI). Оберните вызов в путь, затем вызовите fill() или stroke():
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Circle Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="circleCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('circleCanvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
// Center (100, 100), radius 60, full circle
ctx.arc(100, 100, 60, 0, 2 * Math.PI);
ctx.fillStyle = 'orange';
ctx.fill();
ctx.stroke(); // Add an outline on top of the fill
</script>
</body>
</html>Продвинутые операции с Canvas
Трансформации Canvas
translate, rotate и scale изменяют систему координат canvas, а не отдельные фигуры. Важно: они накапливаются — каждая из них строится на предыдущей, а поворот происходит вокруг текущего начала координат, а не центра фигуры. Чтобы повернуть вокруг точки, сначала выполните translate к этой точке, а затем rotate.
Поскольку трансформации накапливаются, оборачивайте их в ctx.save() / ctx.restore(), чтобы вернуться к предыдущему состоянию, не отменяя каждое преобразование вручную:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Transformation Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="transformCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('transformCanvas');
const ctx = canvas.getContext('2d');
ctx.save(); // Remember the untransformed state
ctx.translate(50, 50); // Move the canvas origin to (50, 50)
ctx.rotate((Math.PI / 180) * 25); // Rotate 25 degrees (converted to radians)
ctx.fillStyle = 'blue'; // Set fill color to blue
ctx.fillRect(0, 0, 100, 50); // Draw at the new, rotated origin
ctx.restore(); // Back to the original coordinate system
</script>
</body>
</html>Анимация с помощью Canvas
В canvas нет встроенной анимации; движение создаётся путём многократной очистки и перерисовки всей поверхности. Функция браузера requestAnimationFrame планирует запуск вашей функции рисования перед следующей перерисовкой — как правило, 60 раз в секунду — и автоматически приостанавливается, когда вкладка скрыта, именно поэтому она предпочтительнее setInterval.
Цикл всегда состоит из трёх шагов: очистить canvas (clearRect), обновить состояние (здесь — позицию x) и нарисовать новый кадр. Если забыть clearRect, вместо движущейся фигуры вы увидите след от всех прошлых кадров.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Animation Example</title>
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="animationCanvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('animationCanvas');
const ctx = canvas.getContext('2d');
let x = 0; // Starting position
function drawFrame() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas for the new frame
ctx.fillStyle = 'green'; // Set the fill color to green
ctx.fillRect(x, 20, 50, 50); // Draw a moving rectangle
x++; // Increment the horizontal position
if (x > canvas.width) {
x = 0;
}
requestAnimationFrame(drawFrame); // Continue the animation
}
drawFrame();
</script>
</body>
</html>Типичные ошибки
- Размытый вывод: несовпадение размера буфера и CSS-размера. Устанавливайте размеры через атрибуты
width/height, а не через CSS, если только вы намеренно не масштабируете. - Фигуры сливаются: если забыть
beginPath()перед новым путём, он продолжит предыдущий. - Линии исчезают: вы вызвали
stroke()илиfill(), но не построили путь, либо нарисовали за пределами canvas. - Анимация оставляет следы: отсутствует
clearRectв начале каждого кадра. - Ничего не отображается: скрипт выполнился до того, как
<canvas>появился в DOM. Разместите скрипт после элемента или запустите его по событиюDOMContentLoaded.
Заключение
Элемент <canvas> предоставляет низкоуровневую поверхность с точностью до пикселя для графики, которую нельзя создать средствами HTML и CSS: диаграммы, редактирование изображений, системы частиц и полноценные игры. API невелик — получить контекст, установить стиль, выдать команду рисования — а анимации — это просто тот же цикл, выполняемый много раз в секунду.
Чтобы углубиться в тему, смотрите тег HTML canvas и основы рисования на canvas для разметки, а также анимации JavaScript и API анимаций на основе requestAnimationFrame для более плавного движения. Чтобы надёжно выбирать canvas, ознакомьтесь с getElement и querySelector.