Canvas 2D — программируемая графика в браузере
См. также: Работа с HTML (DOM) · События · ResizeObserver · Применение JS в вебе · Web API в браузере
Задача
Canvas — HTML-элемент, на котором JavaScript рисует растровую графику: диаграммы, простые игры, редакторы, превью изображений, анимации без тяжёлых библиотек.
Сценарий из учебной практики:
- В разметке —
<canvas width="400" height="300">. - В скрипте — контекст
getContext('2d'). - Рисование — методами
fillRect,stroke,drawImage,fillText. - Обновление кадра —
clearRectи перерисовка; для анимации —requestAnimationFrame.
Canvas — Web API, не часть ECMAScript. Для 3D используют WebGL; для векторной разметки в DOM — SVG.
Разметка и контекст
<canvas id="chart" width="400" height="300">
<p>Ваш браузер не поддерживает canvas</p>
</canvas>
| Атрибут | Роль |
|---|---|
width, height | внутренний размер буфера в пикселях (по умолчанию 300×150) |
| Содержимое внутри тега | fallback для старых браузеров |
const canvas = document.getElementById('chart');
const ctx = canvas.getContext('2d');
console.log(ctx.canvas === canvas); // true
console.log(canvas.width, canvas.height);
Контекст CanvasRenderingContext2D (ctx) — единая точка входа: цвета, линии, пути, текст, картинки. Свойство ctx.canvas возвращает элемент <canvas>.
Если задать canvas { width: 800px; height: 600px; }, а атрибуты width="400" height="300", браузер растянет bitmap — картинка будет размытой. Для Retina умножайте атрибуты на devicePixelRatio или синхронизируйте размеры в ResizeObserver.
Система координат
- Начало координат — левый верхний угол холста.
- Ось X растёт вправо, Y — вниз.
- Единица — пиксель внутреннего буфера (не CSS-пиксель при масштабировании).
Углы для arc() задают в радианах:
const degrees = 90;
const radians = (Math.PI / 180) * degrees;
Заливка, контур, прямоугольники
| Свойство | Назначение |
|---|---|
fillStyle | цвет или градиент заливки (по умолчанию #000) |
strokeStyle | цвет контура |
lineWidth | толщина линии (px) |
lineCap | 'butt', 'round', 'square' |
lineJoin | 'miter', 'bevel', 'round' |
globalAlpha | прозрачность 0.0–1.0 для следующих операций |
| Метод | Назначение |
|---|---|
fillRect(x, y, w, h) | залитый прямоугольник |
strokeRect(x, y, w, h) | контур прямоугольника |
clearRect(x, y, w, h) | очистить область (прозрачный фон) |
ctx.fillStyle = 'rgb(0, 0, 255)';
ctx.fillRect(50, 50, 200, 200);
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
ctx.fillRect(100, 100, 150, 150);
ctx.strokeStyle = 'rgb(0, 0, 255)';
ctx.lineWidth = 3;
ctx.strokeRect(50, 50, 200, 200);
ctx.clearRect(0, 0, canvas.width, canvas.height);
Цвета — в любом формате CSS: 'red', '#ff0000', 'rgb(...)', 'rgba(...)'.
Пути — линии, дуги, фигуры
Сложные фигуры строят контуром (path):
| Метод | Назначение |
|---|---|
beginPath() | начать новый контур |
moveTo(x, y) | перенести «перо», не рисуя |
lineTo(x, y) | отрезок от текущей точки |
arc(x, y, r, start, end, counterclockwise?) | дуга / окружность |
arcTo(x1, y1, x2, y2, radius) | дуга через опорные точки |
quadraticCurveTo(cpx, cpy, x, y) | квадратичная кривая Безье |
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) | кубическая кривая Безье |
rect(x, y, w, h) | добавить прямоугольник к пути |
closePath() | замкнуть контур |
fill() / stroke() | залить или обвести текущий путь |
clip() | маска по контуру — дальнейшая отрисовка только внутри |
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(100, 20);
ctx.lineTo(100, 80);
ctx.closePath();
ctx.strokeStyle = 'rgb(255, 0, 0)';
ctx.lineWidth = 3;
ctx.stroke();
ctx.fillStyle = 'rgb(0, 0, 255)';
ctx.fill();
ctx.beginPath();
ctx.arc(200, 150, 100, 0, Math.PI * 2);
ctx.stroke();
Пунктир: setLineDash([15, 10]), смещение — lineDashOffset. Сброс — setLineDash([]).
Текст
| Свойство / метод | Назначение |
|---|---|
font | строка как в CSS ('bold 16px Verdana') |
textAlign | 'left', 'right', 'center', 'start', 'end' |
textBaseline | 'top', 'middle', 'bottom', 'alphabetic', … |
fillText(text, x, y [, maxWidth]) | залитый текст |
strokeText(text, x, y [, maxWidth]) | контурный текст |
measureText(text).width | ширина строки в px |
ctx.font = 'italic 16pt Verdana';
ctx.fillStyle = 'blue';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('JavaScript', canvas.width / 2, canvas.height / 2);
Изображения — drawImage
Источник — HTMLImageElement, другой <canvas>, <video> или new Image() после загрузки.
const img = new Image();
img.onload = () => {
ctx.drawImage(img, 0, 0); // целиком в (0,0)
ctx.drawImage(img, 0, 0, 200, 100); // масштаб в w×h
ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh); // фрагмент → область
};
img.src = 'photo.jpg';
До события load рисовать нельзя — bitmap пустой. Для <img id="photo"> в DOM достаточно drawImage(document.getElementById('photo'), 0, 0).
Градиенты и текстуры
Линейный градиент:
const lg = ctx.createLinearGradient(0, 0, 200, 0);
lg.addColorStop(0, 'black');
lg.addColorStop(0.5, 'rgba(0, 0, 255, 0.5)');
lg.addColorStop(1, '#ff0000');
ctx.fillStyle = lg;
ctx.fillRect(0, 0, 200, 100);
Радиальный: createRadialGradient(x0, y0, r0, x1, y1, r1) + те же addColorStop.
Паттерн (текстура): createPattern(image, 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat') → присвоить fillStyle или strokeStyle.
Сохранение состояния и трансформации
save() кладёт в стек текущие стили, трансформации и clip; restore() возвращает предыдущие.
| Метод | Назначение |
|---|---|
translate(tx, ty) | сдвиг начала координат |
rotate(angle) | поворот (радианы), вокруг текущего origin |
scale(sx, sy) | масштаб по осям |
ctx.save();
ctx.translate(200, 150);
for (let i = 0; i < 3; i++) {
ctx.rotate(Math.PI / 6);
ctx.strokeRect(-50, -50, 100, 100);
}
ctx.restore();
Без save/restore трансформации накапливаются — типичный источник «уплывшей» графики.
Наложение, тени, прозрачность
| Свойство | Назначение |
|---|---|
globalCompositeOperation | режим смешивания ('source-over', 'destination-over', 'lighter', 'xor', …) |
globalAlpha | общая прозрачность следующих фигур |
shadowOffsetX, shadowOffsetY | смещение тени |
shadowBlur | размытие |
shadowColor | цвет тени |
Тень применяется к следующим операциям рисования (fill, stroke, fillText).
Пиксели — ImageData
| Метод | Назначение |
|---|---|
getImageData(x, y, w, h) | снять фрагмент bitmap |
createImageData(w, h) | пустой буфер |
putImageData(data, x, y) | вывести буфер на холст |
Массив imageData.data — последовательность RGBA байт (0–255) по строкам: [R, G, B, A, R, G, B, A, …].
const imageData = ctx.createImageData(100, 100);
const { data } = imageData;
for (let i = 0; i < data.length; i += 4) {
data[i] = 255; // R
data[i + 1] = 0; // G
data[i + 2] = 0; // B
data[i + 3] = 255; // A
}
ctx.putImageData(imageData, 0, 0);
Подходит для фильтров и процедурной генерации; для обычных диаграмм чаще хватает path API.
Клики и hit-testing
Метод isPointInPath(x, y) проверяет, попадает ли точка в текущий контур — основа выбора фигур мышью.
canvas.addEventListener('click', (event) => {
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
// при CSS-масштабе умножьте на canvas.width / rect.width
ctx.beginPath();
ctx.arc(100, 100, 40, 0, Math.PI * 2);
if (ctx.isPointInPath(x, y)) {
console.log('Попали в круг');
}
});
Координаты события — в CSS-пикселях относительно элемента; при несовпадении с canvas.width нужно масштабировать.
Анимация и перерисовка
function loop(timestamp) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawFrame(timestamp);
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
requestAnimationFrame синхронизирует кадры с частотой экрана и ставит на паузу во вкладке в фоне. Для графиков в flex-layout пересчитывайте размеры в ResizeObserver.
Canvas, SVG и WebGL
| Технология | Когда выбирать |
|---|---|
| Canvas 2D | много пикселей, анимация, игры, растровые эффекты |
| SVG | масштабируемая векторная графика, доступность, CSS к фигурам |
| WebGL | 3D, тяжёлая GPU-графика |
Краткий пример в справочнике BOM — 22.md, графика Canvas; DOM и события — 102.md, 23.md.
Частые ошибки
| Ошибка | Что делать |
|---|---|
Рисование до img.onload | ждать загрузки или decode() |
| Размытый холст на Retina | синхронизировать width/height с devicePixelRatio |
| «Уехали» координаты | оборачивать трансформации в save/restore |
Нет beginPath() между фигурами | контуры сливаются — начинать путь заново |
isPointInPath без учёта CSS-размера | масштабировать координаты клика |
См. также
Другие статьи этого же раздела в боковом меню (как на странице "О разделе"). Основы JavaScript - стандарт ECMAScript, модель выполнения и базовые конструкции языка. JavaScript — это язык программирования, который позволяет создавать интерактивные веб-страницы, серверные приложения и мобильные программы. Для создания массивов используется литеральная нотация. Конструктор Array не применяется. DOM в JavaScript — поиск элементов, формы, выделение текста, атрибуты, создание и изменение HTML. Простые приложения на JavaScript - базовые сценарии, структура кода и быстрый старт с практическими примерами. Расширения файлов определяют способ обработки кода средой выполнения или компилятором. История JavaScript - происхождение языка, ключевые этапы развития и влияние на современный веб. Такое именование представляет собой соглашение между разработчиками. Классический JavaScript не обеспечивает реальной приватности через подчеркивания. JavaScript содержит набор зарезервированных слов, которые имеют специальное значение в языке. Эти слова нельзя использовать в качестве идентификаторов для переменных, функций или классов. Встроенные функции JavaScript - ключевые методы массивов, строк и объектов для повседневной разработки. Подключение скриптов в HTML, структура файла, CommonJS и ESM в Node.js, кэш модулей и package.json. JavaScript используется для создания кроссплатформенных мобильных приложений, которые работают на iOS и Android с использованием единой кодовой базы.Основы JavaScript
Что требуется знать перед началом изучения языка программирования JavaScript
Рекомендации по разработке на JavaScript
Работа с HTML в JavaScript
Простые приложения на JavaScript
Форматы JavaScript
История языка JavaScript
Синтаксис и пунктуация в JavaScript
Ключевые слова языка JavaScript
Встроенные функции JavaScript
Структура и подключение JavaScript-кода
Применение JavaScript в вебе и за его пределами