Как сделать график на Canvas
- 31 июля 2020
Canvas API— это интерфейс для отрисовки графики в браузере. С помощью HTML-элемента <canvas>
можно создавать холст (именно так canvas переводится с английского) и рисовать графику внутри него с помощью JavaScript-кода.
<canvas id="canvas"></canvas>
Используя Canvas, можно создавать игры, анимацию, графику, обрабатывать изображения и решать другие задачи. В этом туториале мы познакомимся с некоторыми базовыми возможностями Canvas и построим несложный график.

В реальной разработке для создания сложных графиков чаще всего используют специальные библиотеки. Например, Chart.js или подобные. Но они сами в большинстве случаев под капотом работают на Canvas, поэтому полезно понимать, как он устроен.
💫 Научитесь создавать анимации
Приходите на наши курсы — изучите самые популярные и интересные техники для создания эффектов на CSS и JavaScript.
Шаг 1. Напишем разметку
Создание графика начнём, конечно, с разметки. Кроме самого элемента <canvas>
мы также сверстаем небольшую форму, которая будет принимать на вход значения. Так мы сможем динамически обновлять график с учётом введённых данных.
<section class="chart">
<h1 class="chart__title">Популярность языков программирования</h1>
<form class="chart__data" action="#" method="POST">
<div class="chart__input-group">
<label class="chart__label" for="javascript">JavaScript</label>
<input
class="chart__input input-field"
type="number"
name="javascript"
id="javascript"
min="0"
max="100"
required
>
<span>%</span>
</div>
<!-- Остальные поля ввода: PHP и Python -->
<input
class="chart__submit-button input-field"
type="submit"
value="Показать график"
>
</form>
<div class="chart__canvas-wrapper">
<canvas id="canvas" width="500" height="250">
Браузер не поддерживает Canvas
</canvas>
</div>
</section>
На что здесь стоит обратить внимание:
- Вместо пути в атрибуте
action
мы оставили заглушку#
. В теории нам может понадобится отправить на сервер данные, введённые пользователем, но пока такая задача не стоит. - Для каждого поля ввода прописали атрибут
name
, в котором отражено его назначение. Например, в нашем случае значениеjavascript
поможет нам использовать полученные данные при отрисовке графика. - Так как значения в полях ввода должны задаваться в процентах, мы установили диапазон допустимых значений с помощью
min
иmax
. - Элементу
<canvas>
прописали размер 500×250 пикселей. По умолчанию он равен 300×150 пикселей. - Внутри
<canvas>
оставили информационное сообщение на случай, если браузер пользователя не поддерживает технологию.
Стилизация элементов будет максимально простой, поэтому не будем её разбирать в рамках туториала. Стили можно посмотреть в файле style.css
в интерактивной демонстрации.
Шаг 2. Соберём данные для графика
Прежде чем рисовать график, нам нужно получить данные из формы. Для этого найдём все поля ввода в форме и создадим на основе введённых значений массив объектов с информацией о каждом элементе графика. В нашем случае это будут языки JavaScript, PHP и Python — три объекта.
const COLORS = [
`#b3d5fc`,
`#98d9d9`,
`#ede493`
];
const inputElements = document.querySelectorAll(`.chart__input`);
const getData = (inputElements) => {
return Array.from(inputElements).map((input, index) => ({
name: input.name,
value: input.value,
color: COLORS[index]
}));
};
const items = getData(inputElements);
Функция getData()
получает на вход коллекцию элементов, преобразует её в массив, а затем на основе каждого элемента создаёт объект с нужными для нашего графика данными: названием (язык программирования), значением (популярность в процентах) и цветом (его будем использовать для рисования столбца на графике).
Для задания цвета мы прибегли к самому примитивному способу — просто завели константу с нужным количеством цветов и присвоили каждому из объектов соответствующий по индексу цвет. В реальной разработке лучше получить цвет другим способом — например, сгенерировать в формате HEX с помощью JavaScript. Тогда получится избежать жёсткой привязки к количеству элементов.
Шаг 3. Подготовим холст
Начнём работу над созданием графика. Из элемента <canvas>
можно получить объект, который называется контекстом отрисовки. Объект содержит свойства и методы для рисования, именно с помощью него можно создавать весь контент внутри Canvas, а также манипулировать им. Для получения объекта контекста нужно воспользоваться методом getContext()
:
const canvas = document.querySelector(`#canvas`);
const ctx = canvas.getContext(`2d`);
В качестве параметра в метод getContext()
мы передали значение 2d
— это контекст отрисовки для работы с двумерной графикой. Именно его мы будем использовать, но есть и другие — например, webgl
для трёхмерной графики.
Шаг 4. Нарисуем график
Свойства и методы контекста отрисовки
Для отрисовки чего-либо на Canvas нужно вызывать соответствующие методы контекста, а для управления параметрами отрисовки — задавать свойства. Мы рассмотрим те из них, которые понадобятся в туториале. Описание остальных можно найти на MDN.
fillStyle
— свойство определяет цвет заливки элементов. Например, текста или геометрической фигуры.font
— свойство задаёт параметры шрифта. Синтаксис такой же, как у CSS-свойстваfont
.fillText(
text, x, y)
— отрисовывает строкуtext
, начиная с указанных координат. Координатаx
определяет расстояние от точки отсчёта по горизонтальной оси, аy
— по вертикальной. Начало отсчёта холста расположено в левом верхнем углу.fillRect(x, y, width, height)
— рисует прямоугольник с заливкой с указанными шириной и высотой, начиная с координатx
иy
.clearRect(x, y, width, height)
— очищает прямоугольник, описанный параметрами. Можно использовать для очистки холста при перерисовке.translate(x, y)
— перемещает начало координат холста наx
по горизонтали иy
по вертикали относительно начального положения.rotate(angle)
— поворачивает систему координат по часовой стрелке на указанный угол. Значение задаётся в радианах.save()
— сохраняет текущее состояние холста, в том числе заданные свойства (например,fillStyle
иfont
).restore()
— применяет сохранённое ранее состояние холста.
Константы и перечисления
Этот пункт можно было бы пропустить и, если нам в коде понадобилось бы значение, например, ширины столбца графика, написать просто 50
. Такие числа в программировании называются «магическими» и считаются плохой практикой в реальной разработке. И для этого есть несколько веских причин. Например, если число нужно будет изменить, то это придётся делать в каждом фрагменте программы, где оно используется. А ещё часто из контекста может быть неясно, что это вообще за число. Эту проблему как раз решают константы с понятным названием. Мы будем привыкать сразу писать хороший код, поэтому вынесем все значения в константы. А если несколько констант относятся к одной сущности, объединим их в перечисления.
const MAX_PERCENTAGE = 100;
const Gap = {
HORIZONTAL: 100,
VERTICAL: 30
}
const BarCoordinate = {
INITIAL_X: 80,
INITIAL_Y: 220
}
const BarSize = {
MAX_HEIGHT: 190,
WIDTH: 50
};
const LabelCoordinate = {
INITIAL_X: 30,
INITIAL_Y: 70
}
const Font = {
SIZE: `18px`,
FAMILY: `Tahoma`
};
Здесь всё, что нам понадобится для рисования графика — начальные координаты элементов, размеры, отступы, параметры шрифта.
Сдвиг системы координат
При отрисовке графика мы будем использовать сочетание методов translate()
и rotate()
:
ctx.translate(0, canvas.height);
ctx.rotate(-Math.PI/2);
Такая комбинация позволяет поменять систему координат по умолчанию, точка отсчёта которой расположена в верхнем левом углу. Код повернёт систему координат на 90 градусов против часовой стрелки и сдвинет вниз на высоту холста. Так она выглядит до и после:

График
Теперь нам нужно воспользоваться методами 2d контекста, которые мы разобрали выше, и нарисовать график на основе данных из формы.
// Получаем на вход items — массив объектов с данными
const renderChart = (items) => {
// Очищаем всю область холста
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Задаём координаты для первого столбца и подписи
let currentBarX = BarCoordinate.INITIAL_X;
let currentLabelY = LabelCoordinate.INITIAL_Y;
// Определяем горизонтальный отступ между соседними столбцами
const gapBetweenBars = BarSize.WIDTH + Gap.HORIZONTAL;
// Проходим в цикле по каждому объекту в массиве с данными
// Для каждого будет нарисован отдельный столбец
for (const item of items) {
// Вычисляем высоту столбца с учётом процентов из данных
const barHeight = (item.value * BarSize.MAX_HEIGHT) / MAX_PERCENTAGE;
// Задаём цвет заливки любых элементов, которые будут создаваться дальше
ctx.fillStyle = item.color;
// Задаём параметры шрифта
ctx.font = `${Font.SIZE} ${Font.FAMILY}`;
// Запоминаем текущие параметры холста
ctx.save();
// Сдвигаем начало коодинат вниз по оси y на величину canvas.height
ctx.translate(0, canvas.height);
// Поворачиваем систему координат на 90 градусов против часовой стрелки
// Math.PI/2 — перевод 90 градусов в радианы
ctx.rotate(-Math.PI/2);
// В изменённой системе координат рисуем текст снизу вверх
ctx.fillText(item.name.toUpperCase(), LabelCoordinate.INITIAL_X, currentLabelY);
// Возвращаемся к изначальной системе координат
ctx.restore();
// Рисуем столбец
// Отрицательное значение — отрисовка снизу вверх
ctx.fillRect(currentBarX, BarCoordinate.INITIAL_Y, BarSize.WIDTH, -barHeight);
// Для следующего столбца обновляем координаты с учётом отступа
currentBarX += gapBetweenBars;
currentLabelY += gapBetweenBars;
}
};
Вы заметили, что в самом начале мы очистили весь холст? Если этого не сделать, изображения будут накладываться друг на друга. В данном случае такой вариант не подходит, потому что мы хотим, чтобы при каждом клике на кнопку «Показать график» происходила перерисовка с учётом новых данных. Именно эту функциональность нам осталось запрограммировать.
Шаг 5. Реализуем отрисовку графика при отправке формы
У нас уже есть функция getData()
, которая возвращает данные, и renderChart()
, способная отрисовать график. Осталось только связать их вместе при клике на кнопку «Показать график».
Все поля ввода мы изначально сгруппировали в форму, поэтому будем отслеживать событие submit
, которое произойдёт при отправке. А когда событие сработает, отрисуем график с учётом введённых данных.
const formElement = document.querySelector(`.chart__data`);
formElement.addEventListener(`submit`, (evt) => {
// Отменяем действие по умолчанию — отправку формы на сервер (которого нет)
evt.preventDefault();
// Отрисовываем график
renderChart(getData(inputElements));
// Сбрасываем значения полей ввода
formElement.reset();
});
Готово! Мы создали простой график на Canvas. Его можно доработать, например, добавив проверку на сумму введённых значений, которая по логике должна быть равна 100%, сейчас это правило можно нарушить. Поэкспериментировать с этим и другими улучшениями можно в нашей интерактивной демонстрации.
Полезности
- Canvas tutorial на MDN.
- HTML5 Canvas cheat sheet. Шпаргалка с основными свойствами и методами 2d контекста, разбитыми на категории.
- The HTML5 Canvas Handbook. Справочник по Canvas с примерами.
- 10 крутых примеров работы HTML5 Canvas. Не самые практичные, но иногда это и не главное.
«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.
Читать дальше

Случайное число из диапазона
Допустим, вам зачем-то нужно целое случайное число от min
до max
. Вот сниппет, который поможет:
function getRandomInRange(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
- Math.random () генерирует случайное число между 0 и 1. Например, нам выпало число
0.54
. - (max — min + 1): определяет количество возможных значений в заданном диапазоне.
10 - 0 + 1 = 11
. Это значит, что у нас есть 11 возможных значений (0, 1, 2, ... 10). - Math.random () * (max — min + 1): умножает случайное число на количество возможных значений:
0.54 * 11 = 5.94
. - Math.floor (): округляет число вниз до ближайшего целого. Так,
Math.floor(5.94) = 5
. - ... + min: смещает диапазон так, чтобы минимальное значение соответствовало
min
. Но в нашем примере, так какmin = 0
, это не изменит результат. Пример:5 + 0 = 5
. - Итак, в нашем примере получилось случайное число 5 из диапазона от 0 до 10.
Чтобы протестировать, запустите:
console.log(getRandomInRange(1, 10)); // Тест
- 7 сентября 2023

В чём разница между var и let
Если вы недавно пишете на JavaScript, то наверняка задавались вопросом, чем отличаются var
и let
, и что выбрать в каждом случае. Объясняем.
var
и let
— это просто два способа объявить переменную. Вот так:
var x = 10;
let y = 20;
Переменная, объявленная через var
, доступна только внутри «своей» функции, или глобально, если она была объявлена вне функции.
function myFunction() {
var z = 30;
console.log(z); // 30
}
myFunction();
console.log(z); // ReferenceError
Это может создавать неожиданные ситуации. Допустим, вы создаёте цикл в функции и хотите, чтобы переменная i
осталась в этой функции. Если вы используете var
, эта переменная «утечёт» за пределы цикла и будет доступна во всей функции.
Переменные, объявленные с помощью let
доступны только в пределах блока кода, в котором они были объявлены.
if (true) {
let a = 40;
console.log(a); // 40
}
console.log(a); // ReferenceError
В JavaScript блок кода — это участок кода, заключённый в фигурные скобки {}
. Это может быть цикл, код в условном операторе или что-нибудь ещё.
if (true) {
let blockScoped = "Я виден только здесь";
console.log(blockScoped); // "Я виден только здесь"
}
// здесь переменная blockScoped недоступна
console.log(blockScoped); // ReferenceError
Если переменная j
объявлена в цикле с let
, она останется только в этом цикле, и попытка обратиться к ней за его пределами вызовет ошибку.
- 30 августа 2023

Быстрый гайд по if, else, else if в JavaScript
Допустим, вы собираетесь идти на прогулку. Если на улице солнечно, вы возьмёте с собой солнечные очки.
Это можно описать с помощью оператора if
.
let weather = "sunny";
if (weather === "sunny") {
console.log("Возьму солнечные очки");
}
А если погода не солнечная, а, скажем, дождливая, вы возьмете зонт.
Этот сценарий можно описать с помощью if-else
.
let weather = "rainy";
if (weather === "sunny") {
console.log("Возьму солнечные очки");
} else {
console.log("Возьму зонт");
}
Условный оператор if-else if-else
Теперь представим, что у вас есть несколько вариантов транспорта для дороги на работу: машина, велосипед, общественный транспорт. Выбор будет зависеть от различных условий, например, погоды и времени суток. Логично, что в дождь безопаснее ехать на автобусе, а в хорошую погоду можно прокатиться на машине или велосипеде, если утро и пробки. То есть схема такая:
И всё это очень легко описывается кодом:
let weather = "sunny";
let time = "morning";
if (weather === "rainy") { // если дождь, то только так
console.log("Еду на автобусе");
} else if (time === "morning") { // если не дождь и утро
console.log("Еду на велике мимо пробок");
} else { // если второе не дождь и не утро
console.log("Еду на машине");
}
Ветвление только может показаться сложным, но вообще оно очень логичное, если понять, какие действия после каких условий выполняются. Разберитесь один раз и поймёте на всю жизнь, 100%.
🐈
- 30 августа 2023

Как исправить ошибки SyntaxError в JavaScript
Ошибки SyntaxError появляются, если разработчик нарушил правила синтаксиса JavaScript, например, пропустил закрывающую скобку или точку с запятой. Давайте посмотрим, что означает каждая ошибка и в чём может быть проблема.
- 14 июля 2023

Ошибка TypeError: что это и как её исправить
Ошибки TypeError появляются, когда разработчики пытаются выполнить операцию с неправильным типом данных. Давайте разберём несколько примеров: почему появилась ошибка и как её исправить.
- 7 июля 2023

3 способа объявить функцию в JavaScript
Функции в JavaScript можно объявить тремя способами: через декларативное объявление, функциональное выражение или с помощью стрелок. Звучит сложно, но на самом деле всё совсем не так.
- 30 июня 2023

Как сделать простой слайдер на HTML и JavaScript
Вы сверстали сайт и сделали его красивым с помощью CSS. Осталось добавить интерактива, и можно добавлять проект в портфолио.
«Оживить» на сайте можно что угодно: меню, модальные окна, корзину, пагинацию… В этой статье мы разберём слайдер — посмотрим, как его сделать на чистом JavaScript. Слайдер пригодится для раздела с отзывами, фотографиями сотрудников, изображениями товаров или чего-нибудь ещё — всё зависит только от вашей фантазии и проекта.
☝ Мы покажем лишь один из возможных вариантов. Это не эталонное решение, да в разработке и не бывает единственно верного способа решить задачу. Но код точно работает, поэтому можете скопировать его в свой проект.
- 20 июня 2023

Полезные команды для работы с Node.js
Перед тем как рассматривать полезные команды при работе с Node.js, её необходимо установить.
Команды помогают узнать версию Node.js,
node -h
— показывает список всех доступных команд Node.js.
node -v
, node --version
— показывает установленную версию Node.js.
npm -h
— показывает список всех доступных команд пакетного менеджера npm
.
npm -v
, npm --version
— показывает установленную версию npm
.
Команда npm update npm -g
позволяет обновить версию npm
.
npm list --depth=0
показывает список установленных пакетов.
Команда npm outdated --depth=0
покажет список установленных пакетов, которые требуют обновления. Если все пакеты обновлены, список будет пустым.
npm install package
— позволяет установить любой пакет по его имени. Если при этом к команде добавить префикс -g
пакет будет установлен глобально на весь компьютер.
Команда npm i package
является укороченной альтернативой предыдущей команды.
Если вы хотите установить конкретную версию пакета, воспользуйтесь префиксом @
с номером версии. Например, npm install package@1.0.1
.
npm uninstall package
— удаляет установленный пакет по имени.
Команда npm list package
— покажет версию установленного пакета, а команда npm view package version
— последнюю версию пакета, которая существует.
Для работы с пакетным менеджером также пригодится файл package.json
, который должен лежать в директории, с которой происходит работа в консоли.
Он содержит различные мета-данные, например, имя проекта, версия, описания и автор. Также он содержит список зависимостей, которые будут установлены, если вызвать из этой папки команду npm install
.
Кроме этого он ещё имеет скрипты, которые вызывают другие команды консоли. Например, для этого файла вызов команды npm start
вызовет запуск задачи Grunt с именем dev
. А команда npm run build
вызовет скрипт build
, который запустит задачу в Grunt с именем build
.
Во время работы часто возникает необходимость установить некоторые пакеты. Если установить пакет с префиксом --save
, то он автоматически запишется в package.json
в раздел dependencies
. Такая же команда с префиксом --save-dev
запишет пакет в раздел devDependencies
.
Что такое nvm
nvm (илиNode Version Manager) — утилита, которая позволяет быстро менять версии Node.js.
Чтобы её установить, достаточно запустить скрипт
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
Теперь можно установить последнюю версию Node.js, например,5.0
с помощью команды nvm install 5.0
. Чтобы начать использовать её, введите команду nvm use 5.0
. Таким образом, можно быстро переключаться между версиями, например, для тестирования.
- 8 июня 2023

Как составлять регулярные выражения
Регулярное выражение — это последовательность символов (селекторов). Оно используется для поиска и обработки строк, слов, чисел и других текстовых данных.
Регулярные выражения выручают при решении разных задач. Например, с их помощью легко искать и менять строки в коде. Но чаще всего регулярные выражения используют для валидации форм. Давайте посмотрим, как это делать.
- 5 июня 2023

Проверка типа интерфейса в TypeScript
Проверка типов интерфейса — одна из ключевых возможностей TypeScript. Она помогает убедиться, что объект или класс содержат необходимый набор свойств и методов, указанных в интерфейсе. Благодаря проверке типов вы можете писать более надёжный код, ведь часть ошибок будет найдена ещё на этапе компиляции.
- 30 мая 2023