О чем этот пример
По умолчанию Phaser сам создаёт элемент <canvas> и встраивает его в DOM. Но иногда вам нужно взять под полный контроль над контейнером игры — например, для интеграции с другими библиотеками, кастомного CSS-оформления или сложной вёрстки. В этой статье мы разберём, как передать Phaser уже существующий элемент <canvas> и настроить его под свои нужды. Этот подход открывает возможность тонкой настройки окружения игры.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('pic', 'assets/pics/baal-loader.png');
}
create ()
{
this.add.image(400, 300, 'pic');
}
}
const myCustomCanvas = document.createElement('canvas');
myCustomCanvas.id = 'myCustomCanvas';
myCustomCanvas.style = 'border: 8px solid red';
document.body.appendChild(myCustomCanvas);
// const myCustomContext = myCustomCanvas.getContext('2d');
const config = {
type: Phaser.CANVAS,
parent: 'phaser-example',
width: 800,
height: 600,
canvas: document.getElementById('myCustomCanvas'),
scene: Example
};
const game = new Phaser.Game(config);
Зачем нужен кастомный canvas?
Стандартное поведение Phaser удобно для быстрого старта: вы задаёте parent в конфиге, и движок сам создаёт и размещает элемент. Однако в реальных проектах часто требуется:
- Вручную задать id, class или стили для точного позиционирования.
- Встроить игру в сложную, уже существующую структуру DOM.
- Использовать один canvas для Phaser и других графических операций (например, рисование поверх игры).
- Контролировать момент создания элемента (например, после загрузки определённых данных).
Передав свой canvas, вы получаете полную власть над этим элементом до инициализации игры.
Создание и подготовка своего элемента <canvas>
Всё начинается с создания элемента через DOM API. Мы можем задать ему атрибуты и стили, а затем добавить в тело документа (или любой другой контейнер).
const myCustomCanvas = document.createElement('canvas');
myCustomCanvas.id = 'myCustomCanvas';
myCustomCanvas.style = 'border: 8px solid red';
document.body.appendChild(myCustomCanvas);
Здесь мы создаём элемент, присваиваем ему идентификатор myCustomCanvas и добавляем красную рамку толщиной 8 пикселей для наглядности. После этого элемент становится частью DOM-дерева. Важно сделать это *до* создания экземпляра игры Phaser.Game, иначе движок не найдёт его по id.
Настройка конфига Phaser для работы с кастомным canvas
Ключевой момент — указать созданный canvas в объекте конфигурации игры. Для этого используется свойство canvas. Одновременно мы задаём type: Phaser.CANVAS, чтобы явно указать рендерер.
const config = {
type: Phaser.CANVAS,
parent: 'phaser-example',
width: 800,
height: 600,
canvas: document.getElementById('myCustomCanvas'),
scene: Example
};
Обратите внимание: мы находим элемент по id, который задали ранее. Свойство parent здесь всё ещё можно указать — оно будет использоваться для расчёта масштабирования и выравнивания, но сам canvas создан не будет. Phaser возьмёт переданный элемент и начнёт отрисовку в него.
Что происходит внутри сцены?
Код сцены остаётся абсолютно неизменным. Phaser работает с переданным canvas как с родным. В примере загружается и отображается изображение.
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('pic', 'assets/pics/baal-loader.png');
}
create ()
{
this.add.image(400, 300, 'pic');
}
}
Метод this.add.image выводит картинку в координаты (400, 300) относительно холста. Все операции отрисовки будут применены к нашему кастомному элементу с красной рамкой. Это демонстрирует, что интеграция происходит прозрачно для игровой логики.
Важные технические детали и ограничения
1. **Порядок операций**: Создайте и добавьте canvas в DOM *до* вызова new Phaser.Game(config). Иначе document.getElementById вернёт null.
2. **Контекст рендеринга**: В исходнике есть закомментированная строка // const myCustomContext = myCustomCanvas.getContext('2d');. Получив контекст до инициализации Phaser, вы можете нарушить внутреннюю работу движка. Если нужен гибридный рендеринг, планируйте его аккуратно.
3. **Размеры**: Ширина и высота в конфиге (width, height) определяют внутреннее разрешение игры. Физические размеры canvas на странице можно дополнительно контролировать через CSS, но это может привести к растяжению. Лучше задавать размеры в JavaScript или следить за соотношением сторон.
4. **Рендерер**: Мы используем Phaser.CANVAS. Для WebGL-рендеринга (Phaser.WEBGL) подход аналогичен, но убедитесь, что браузер поддерживает WebGL в данном canvas.
Что попробовать дальше
Использование своего элемента <canvas> в Phaser — мощный приём для продвинутой интеграции игры в веб-страницу. Вы получаете полный контроль над DOM-элементом, сохраняя всю функциональность движка.
**Идеи для экспериментов:**
- Попробуйте динамически менять размеры canvas после создания игры, используя game.scale.resize().
- Встройте несколько независимых экземпляров Phaser в разные canvas на одной странице.
- Навесьте на canvas обработчики событий мыши или клавиатуры поверх игровых.
- Используйте кастомный canvas внутри Shadow DOM для изоляции стилей.
