В JavaScript объект <canvas> предоставляет
низкоуровневый интерфейс для рисования графики. Основной способ
взаимодействия с канвой — через метод getContext('2d'),
который возвращает объект контекста рисования 2D. Этот контекст содержит
все методы и свойства, необходимые для работы с графикой, от простых
фигур до управления пикселями.
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
Метод getContext('2d') создаёт объект
CanvasRenderingContext2D, который предоставляет интерфейс
для рисования прямоугольников, линий, текста, а также для работы с
изображениями и пикселями.
Прямоугольники:
ctx.fillStyle = 'red'; // цвет заливки
ctx.fillRect(10, 10, 100, 50); // x, y, width, height
ctx.strokeStyle = 'blue'; // цвет обводки
ctx.strokeRect(10, 70, 100, 50);
Линии и кривые:
ctx.beginPath();
ctx.moveTo(50, 150);
ctx.lineTo(150, 150);
ctx.stroke();
beginPath() начинает новый путь,
moveTo(x, y) задаёт стартовую точку,
lineTo(x, y) добавляет линию к пути, а
stroke() рисует контур.
Кривые Безье:
ctx.beginPath();
ctx.moveTo(50, 200);
ctx.bezierCurveTo(70, 180, 130, 220, 150, 200);
ctx.stroke();
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) создаёт
кубическую кривую с двумя контрольными точками.
Для прямой манипуляции пикселями используется объект
ImageData, который хранит данные в формате RGBA (каждый
пиксель — 4 байта: красный, зелёный, синий, альфа).
Получение и изменение пикселей:
// Считывание пикселей
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // Uint8ClampedArray
// Изменение всех пикселей: инверсия цветов
for (let i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i]; // красный
data[i + 1] = 255 - data[i + 1]; // зелёный
data[i + 2] = 255 - data[i + 2]; // синий
// data[i + 3] — альфа, оставляем без изменений
}
// Применение изменений обратно на canvas
ctx.putImageData(imageData, 0, 0);
getImageData(x, y, width, height) возвращает пиксели
указанной области, а putImageData(imageData, x, y) рисует
их обратно на канву.
Для доступа к конкретному пикселю используется индекс в массиве
data:
function setPixel(x, y, r, g, b, a = 255) {
const index = (y * canvas.width + x) * 4;
data[index] = r;
data[index + 1] = g;
data[index + 2] = b;
data[index + 3] = a;
}
// Пример: закрасить верхний левый пиксель красным
setPixel(0, 0, 255, 0, 0);
ctx.putImageData(imageData, 0, 0);
Важно учитывать, что массив data — одномерный, а каждый
пиксель занимает 4 позиции: [R, G, B, A]. Индекс пикселя
(x, y) вычисляется как
(y * ширина + x) * 4.
Прямой доступ к пикселям позволяет создавать собственные градиенты, шум или текстуры:
for (let y = 0; y < canvas.height; y++) {
for (let x = 0; x < canvas.width; x++) {
const value = Math.floor((x / canvas.width) * 255); // горизонтальный градиент
setPixel(x, y, value, value, value, 255);
}
}
ctx.putImageData(imageData, 0, 0);
Градиент создаётся вычислением значения каждого канала цвета для конкретной позиции.
Преимущества пиксельного уровня:
Недостатки:
Стандартные методы рисования:
Серый фильтр:
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg;
data[i + 1] = avg;
data[i + 2] = avg;
}
ctx.putImageData(imageData, 0, 0);
Прозрачность и альфа-композиция:
for (let i = 0; i < data.length; i += 4) {
data[i + 3] = 128; // полупрозрачный
}
ctx.putImageData(imageData, 0, 0);
Эти приёмы позволяют создавать эффекты наложения и фильтров без использования внешних библиотек.
getImageData, чтобы не перегружать память.requestAnimationFrame для анимации при
манипуляциях на пиксельном уровне, чтобы сохранить
производительность.