Когда CSS-анимации уже не справляются, а тысячи независимо движущихся частиц нужны прямо сейчас, на сцену выходит PixiJS — один из самых быстрых 2D-рендереров для браузера. Построенный на WebGL (а в версии 8 ещё и на WebGPU), он умеет отрисовывать десятки тысяч объектов за кадр, почти не нагружая CPU. Если вы делаете интерактивную визуализацию, браузерную игру или хотите добавить на сайт что-то, что заставит пользователя сказать «как это сделано?» — вы попали по адресу.
Это пошаговое руководство по обучению PixiJS 8 с нуля: от установки до фильтров, с живыми примерами прямо на странице.
Что такое PixiJS и когда его применять
PixiJS — это 2D-рендерер, а не игровой движок. Он не управляет физикой, уровнями или состоянием игры — только рисует. И делает это исключительно хорошо: WebGL-рендеринг, пакетная обработка спрайтов, текстурные атласы, фильтры через шейдеры.
Вот где PixiJS уместен, а где — избыточен:
- Берите PixiJS, если нужны: частицы, сложные интерактивные анимации, Canvas-игры, визуализация данных в реальном времени, generative art (алгоритмическое искусство — визуальные образы и анимации, созданные кодом, а не нарисованные вручную).
- Не берите PixiJS для обычных UI-переходов и анимаций — там хватит CSS или Anime.js.
PixiJS 8 работает только там, где браузер поддерживает аппаратное ускорение графики. Большинство современных устройств его поддерживают, но около 1–2% пользователей (старые браузеры, корпоративные машины с заблокированным GPU) увидят ошибку вместо анимации. Если аудитория широкая — добавьте проверку и показывайте статичную картинку как запасной вариант.
Установка PixiJS
CDN — быстрый старт. Добавьте скрипт перед закрывающим </body> . Библиотека доступна через глобальный объект PIXI :
<!-- Вставьте перед закрывающим тегом </body> -->
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8/dist/pixi.min.js"></script> npm — для проектов со сборщиком
# Установка пакета
npm install pixi.js С ES-модулями можно импортировать только то, что нужно — это уменьшает финальный бандл:
// Импортируем только нужные классы — лишнее в бандл не попадёт
import { Application, Sprite, Graphics, Assets, Text } from 'pixi.js'; Первое приложение на PixiJS
Основа любого PixiJS-проекта — объект Application . Он создаёт холст, запускает рендерер и игровой цикл. Инициализация асинхронная: сначала создаём экземпляр, затем вызываем app.init() и дожидаемся результата:
const app = new PIXI.Application();
await app.init({
width: 800,
height: 600,
background: 0x1a1a2e, // цвет фона в hex
antialias: true,
resolution: window.devicePixelRatio || 1,
autoDensity: true, // масштабирование под Retina
});
// app.canvas — стандартный HTMLCanvasElement
document.body.appendChild(app.canvas); После этого на странице появится тёмно-синий прямоугольник 800×600 пикселей — это и есть холст. Пока он пустой, но именно на него мы будем добавлять фигуры, спрайты и анимации в следующих разделах.
Два параметра отвечают за чёткость на Retina-экранах: resolution задаёт соотношение пикселей устройства к CSS-пикселям, а autoDensity: true автоматически масштабирует стили холста — без этой пары на Retina картинка будет размытой.
Если нужно, чтобы холст занимал всё окно браузера и подстраивался при изменении его размера, вместо фиксированных width и height используйте resizeTo: window :
await app.init({
background: 0x0d0d1a,
resizeTo: window, // холст всегда во весь экран
autoDensity: true,
resolution: window.devicePixelRatio || 1,
}); Добавим на холст несколько фигур — и получим первую живую сцену. Каждая фигура — объект PIXI.Graphics , который добавляется на сцену через app.stage.addChild() . Анимация — игровой цикл app.ticker.add() , который вызывается на каждом кадре:
// Создаём фигуру — круг с центром в (0, 0) и радиусом 30, синего цвета
const circle = new PIXI.Graphics();
circle.circle(0, 0, 30).fill({ color: 0x5e9fd8 });
// Задаём позицию на холсте
circle.x = 100;
circle.y = 80;
// Добавляем на сцену — без этого фигура не появится на экране
app.stage.addChild(circle);
// Скорость движения по горизонтали (пикселей за кадр)
let vx = 0.6;
// Каждый кадр сдвигаем круг вправо
app.ticker.add((time) => {
circle.x += vx * time.deltaTime;
// Когда круг уходит за правый край — возвращаем его влево
if (circle.x > app.screen.width) circle.x = 0;
}); Вот как это выглядит с несколькими фигурами одновременно:
Graphics API — рисуем фигуры
Если вам не нужны картинки — фигуры можно нарисовать прямо кодом. Для этого в PixiJS есть PIXI.Graphics : создаёте объект, описываете форму (круг, прямоугольник, линия) и говорите, какого цвета должна быть заливка или обводка.
Логика простая: сначала описываем форму, затем вызываем .fill() для заливки цветом или .stroke() для обводки. Один объект Graphics может содержать сразу несколько фигур — это удобно и работает быстро:
const gfx = new PIXI.Graphics();
// Круг: центр (200, 150), радиус 60, синяя заливка
gfx.circle(200, 150, 60)
.fill({ color: 0x5e9fd8 });
// Прямоугольник: позиция (50, 40), размер 120×80, оранжевый с жёлтой обводкой
gfx.rect(50, 40, 120, 80)
.fill({ color: 0xe8734a })
.stroke({ width: 3, color: 0xffd060 });
// Треугольник: массив точек [x1, y1, x2, y2, x3, y3, ...]
gfx.poly([300, 50, 360, 150, 240, 150])
.fill({ color: 0x66e888 });
// Линия: от точки (0, 0) до (400, 300)
gfx.moveTo(0, 0)
.lineTo(400, 300)
.stroke({ width: 2, color: 0xffffff, alpha: 0.3 });
// Добавляем все фигуры на сцену одним вызовом
app.stage.addChild(gfx); Ниже — живой пример: три планеты, нарисованные через PIXI.Graphics , вращаются вокруг звезды. Орбиты — тонкие окружности с прозрачной обводкой, без заливки. Весь этот эффект — только код, никаких изображений:
Ticker — сердце анимации
Чтобы что-то двигалось на экране, нужен цикл, который обновляет позиции объектов снова и снова — в идеале 60 раз в секунду. В PixiJS это делает app.ticker : передайте ему функцию, и она будет вызываться на каждом кадре.
Внутри функции доступен параметр time.deltaTime — он показывает, сколько «условных кадров» прошло с предыдущего вызова. На быстром устройстве при 60 fps это значение равно 1, на медленном при 30 fps — около 2. Умножайте скорость движения на deltaTime , и анимация будет выглядеть одинаково на любом устройстве:
app.ticker.add((time) => {
// Двигаем спрайт вправо и поворачиваем
// time.deltaTime выравнивает скорость на разных устройствах
sprite.x += 2 * time.deltaTime;
sprite.rotation += 0.03 * time.deltaTime;
});
// Ограничить максимальный FPS (например, для экономии заряда на мобильных)
app.ticker.maxFPS = 30;
// Поставить анимацию на паузу и возобновить
app.ticker.stop();
app.ticker.start(); Ещё один полезный параметр — time.lastTime : это количество миллисекунд с момента старта приложения. Удобно для плавных колебаний — например, чтобы объект медленно покачивался вверх-вниз: Math.sin(time.lastTime / 1000) .
Спрайты и текстуры
Спрайт — это картинка на сцене. Чтобы показать изображение в PixiJS, нужно сделать два шага: сначала загрузить файл как текстуру, затем создать из неё спрайт и добавить на сцену. Загрузка асинхронная — PixiJS сам следит за кешем, так что одна картинка загружается только один раз:
// Загружаем изображение — результат сохраняется в кеше автоматически
const texture = await PIXI.Assets.load('path/to/image.png');
// Создаём спрайт из загруженной картинки
const sprite = new PIXI.Sprite(texture);
// anchor задаёт «точку крепления» спрайта
// 0.5, 0.5 — центр: спрайт будет вращаться вокруг своей середины
// 0, 0 — верхний левый угол (значение по умолчанию)
sprite.anchor.set(0.5);
// Размещаем по центру холста
sprite.x = app.screen.width / 2;
sprite.y = app.screen.height / 2;
// Добавляем на сцену
app.stage.addChild(sprite); Если картинки нет, а фигуру нарисовали кодом через Graphics — её тоже можно превратить в текстуру. Это особенно полезно, когда нужно много одинаковых объектов: все они будут использовать одну текстуру в памяти, а не хранить каждый свою копию:
// Рисуем кружок и превращаем его в текстуру
const shape = new PIXI.Graphics();
shape.circle(0, 0, 20).fill({ color: 0xffffff });
const texture = app.renderer.generateTexture(shape);
// Создаём 1000 спрайтов — все используют одну текстуру в памяти
for (let i = 0; i < 1000; i++) {
const dot = new PIXI.Sprite(texture);
dot.anchor.set(0.5);
// Случайная позиция в пределах холста
dot.x = Math.random() * app.screen.width;
dot.y = Math.random() * app.screen.height;
// tint окрашивает спрайт в нужный цвет без создания новой текстуры
dot.tint = Math.random() * 0xffffff;
app.stage.addChild(dot);
} tint — простой способ перекрасить спрайт, не создавая отдельную текстуру для каждого цвета. Значение 0xffffff означает «без окраски», любое другое — смешивается с исходными пикселями картинки.
Контейнеры и граф сцены
Представьте, что вы собираете корабль из нескольких деталей: корпус, пушка, парус. Чтобы передвинуть корабль — нужно двигать каждую деталь отдельно. Неудобно. Вместо этого можно сложить все детали в один «невидимый ящик» — PIXI.Container — и двигать уже его. Все вложенные объекты будут следовать за контейнером автоматически:
// Создаём контейнер — он сам по себе невидим, это просто «группа»
const ship = new PIXI.Container();
const hull = new PIXI.Sprite(hullTexture);
const cannon = new PIXI.Sprite(cannonTexture);
// Позиция пушки задаётся относительно корабля, а не экрана
cannon.x = 40;
// Добавляем детали в контейнер
ship.addChild(hull, cannon);
// Добавляем контейнер на сцену
app.stage.addChild(ship);
// Теперь достаточно двигать ship — все детали следуют за ним
app.ticker.add((time) => {
ship.x += 1 * time.deltaTime;
ship.rotation += 0.005 * time.deltaTime; // весь корабль поворачивается вместе
}); Кстати, app.stage — это тоже контейнер, самый верхний. Всё, что вы добавляете на сцену через addChild() , попадает в него. Если объект не добавлен в app.stage (напрямую или через вложенный контейнер) — он не появится на экране. Убрать объект со сцены можно двумя способами: container.removeChild(sprite) — временно скрыть, или sprite.destroy() — удалить полностью и освободить память.
Текст в PixiJS
Чтобы вывести надпись на холст, используйте PIXI.Text . Передайте строку и объект со стилями — размер, цвет, шрифт, тень, обводка. Менять текст можно в любой момент, просто присвоив новое значение свойству .text :
const label = new PIXI.Text({
text: 'Game Over',
style: {
fontSize: 64,
fill: 0xffffff, // цвет текста
fontWeight: 'bold',
fontFamily: 'Arial, sans-serif',
stroke: { color: 0xff0000, width: 4 }, // красная обводка
dropShadow: {
color: 0x000000, // чёрная тень
blur: 6,
distance: 4,
angle: Math.PI / 4, // угол 45°
},
},
});
// Размещаем по центру холста
label.anchor.set(0.5);
label.x = app.screen.width / 2;
label.y = app.screen.height / 2;
app.stage.addChild(label);
// Поменять текст в любой момент — просто присвоить новое значение
label.text = 'You Win!'; Если на сцене много текстовых объектов, которые часто меняются — счётчики очков, ярлыки, подписи к объектам — присмотритесь к PIXI.BitmapText . Он работает быстрее, потому что использует готовое изображение шрифта вместо того, чтобы рендерить каждую букву заново. Вот как выглядит PIXI.Text на практике:
Интерактивность
По умолчанию объекты на холсте не реагируют на мышь или касание — они просто рисуются. Чтобы объект начал отвечать на действия пользователя, нужно включить для него события одной строкой: eventMode = "static" . После этого можно вешать обработчики — так же, как на обычные DOM-элементы:
const circle = new PIXI.Graphics();
circle.circle(0, 0, 40).fill({ color: 0x5e9fd8 });
circle.x = 200;
circle.y = 150;
// Включаем обработку событий для этого объекта
circle.eventMode = 'static';
// Меняем курсор при наведении — как у обычной кнопки
circle.cursor = 'pointer';
// Наводим — увеличиваем
circle.on('pointerover', () => { circle.scale.set(1.2); });
// Уводим — возвращаем размер
circle.on('pointerout', () => { circle.scale.set(1.0); });
// Нажимаем — делаем полупрозрачным
circle.on('pointerdown', () => { circle.alpha = 0.6; });
// Отпускаем — возвращаем непрозрачность
circle.on('pointerup', () => { circle.alpha = 1.0; });
// Клик (или тап на телефоне)
circle.on('pointertap', () => { console.log('Нажали!'); });
app.stage.addChild(circle); Все события начинаются с префикса pointer — это удобно: они одинаково работают и на компьютере (мышь), и на телефоне (касание). Не нужно писать два отдельных обработчика. Попробуйте сами — понажимайте на кружки:
PixiJS определяет, попал ли курсор в объект, по его прямоугольной области. Для круга или сложной фигуры это может работать неточно — курсор будет реагировать на углы прямоугольника, которые визуально пустые. Чтобы задать точную зону клика, используйте hitArea: sprite.hitArea = new PIXI.Rectangle(x, y, w, h).
Фильтры в PixiJS
Фильтры — это визуальные эффекты, которые применяются поверх любого объекта или целой сцены. Размытие, обесцвечивание, яркость, сепия — всё это включается буквально одной строчкой кода. PixiJS берёт изображение уже нарисованного объекта и пропускает его через эффект перед выводом на экран.
// Размытие — чем больше strength, тем сильнее размыто
const blur = new PIXI.BlurFilter({ strength: 8 });
// Цветовой фильтр — меняет яркость, контраст, цвет
const color = new PIXI.ColorMatrixFilter();
color.grayscale(1, false); // сделать чёрно-белым
// color.brightness(1.5); // яркость +50%
// color.sepia(false); // эффект старой фотографии
// color.saturate(0.5, true); // приглушить насыщенность
// Применяем фильтр к спрайту — можно сразу несколько
sprite.filters = [blur, color];
// Или ко всей сцене разом
app.stage.filters = [blur];
// Убрать все фильтры
sprite.filters = null; Фильтры удобны, но их не стоит использовать без меры: каждый фильтр заставляет браузер проделать дополнительную работу при отрисовке каждого кадра. Старайтесь не вешать больше 2–3 фильтров на один объект, а на объектах, которые скрыты или вышли за край экрана, фильтры лучше отключать совсем — это заметно ускоряет работу на слабых устройствах.
В стандартной поставке фильтров уже немало, но если хочется большего — загляните в пакет @pixi/filters: там собраны ещё два десятка готовых эффектов — свечение, капли дождя, имитация CRT-монитора, хроматическая аберрация и другие.
Итого: что умеет PixiJS
Изучение PixiJS 8 начинается быстро: API продуман, живые примеры работают сразу, документация подробная. Асинхронная инициализация, переработанный Graphics API, WebGPU-рендерер — это не просто косметика, а реальные улучшения, которые ощущаются на практике.
- Для статики — Graphics с .fill() и .stroke() .
- Для анимации — app.ticker.add() с учётом time.deltaTime .
- Для изображений — Assets.load() + Sprite .
- Для текста — PIXI.Text или BitmapText для высоконагруженных сцен.
- Для событий — eventMode = "static" и pointer* .
Если хочется анимировать DOM-элементы без WebGL — посмотрите на Anime.js: анимации на JavaScript. А для визуализации данных с графиками и интерактивными диаграммами стоит обратить внимание на D3.js — они с PixiJS прекрасно дополняют друг друга.
Освоили основы? В второй части идём дальше: десятки тысяч частиц через ParticleContainer, рисование прямо в текстуру, режимы смешивания для огня и свечения, покадровая анимация и собственные GLSL-шейдеры.