W3docs

Рисование на 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>
Result

Очистка части 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>
Result

Рисование путей (линии и произвольные фигуры)

Прямоугольники удобны, но большинство рисунков строится с использованием путей — последовательностей точек, соединённых линиями или кривыми. Путь формируется набором методов и не становится видимым до вызова 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>
Result

Пример: треугольник

Последовательные вызовы 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>
Result

Рисование окружностей и дуг

Метод 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>
Result

Чтобы нарисовать полукруг (полуокружность), завершите дугу на Math.PI радиан вместо 2 * Math.PI.

Что изучить дальше

Освоив фигуры и пути, исследуйте остальные разделы руководства по Canvas:

Практика

Практика
Что возвращает вызов getContext('2d') на элементе canvas?
Что возвращает вызов getContext('2d') на элементе canvas?
Практика
Почему следует вызывать beginPath() перед рисованием каждой новой фигуры?
Почему следует вызывать beginPath() перед рисованием каждой новой фигуры?
Was this page helpful?