W3docs

Введение в 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. Углы измеряются в радианах, и полная окружность составляет радиан, поэтому от 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-экранах.

Связанные главы

Практика

Практика
Каковы характеристики и способы использования HTML Canvas?
Каковы характеристики и способы использования HTML Canvas?
Was this page helpful?