Принцип масштабирования 9-slice

При создании пользовательских интерфейсов в 2D-приложениях часто возникает необходимость масштабировать панели, кнопки, окна и другие элементы произвольного размера. Простое изменение width и height у спрайта в PixiJS приводит к искажению изображения: углы растягиваются, рамки становятся непропорциональными, декоративные элементы теряют форму.

Для решения этой задачи используется принцип 9-slice (nine-slice scaling) — способ масштабирования, при котором изображение делится на девять областей, каждая из которых масштабируется по своим правилам.


Суть метода 9-slice

Исходное изображение делится на сетку 3×3:

+----+-----------+----+
| TL |     T     | TR |
+----+-----------+----+
| L  |   CENTER  | R  |
+----+-----------+----+
| BL |     B     | BR |
+----+-----------+----+

Где:

  • TL, TR, BL, BR — углы (не масштабируются)
  • T, B — верхняя и нижняя грани (масштабируются по горизонтали)
  • L, R — левая и правая грани (масштабируются по вертикали)
  • CENTER — центральная область (масштабируется по обеим осям)

Основные принципы

  1. Углы сохраняют исходный размер
  2. Грани растягиваются только в одном направлении
  3. Центр заполняет всё оставшееся пространство

Благодаря этому кнопка или панель может принимать любые размеры без визуальных искажений рамки.


Реализация в PixiJS

В PixiJS поддержка 9-slice реализована через класс:

PIXI.NineSlicePlane

Этот класс основан на PIXI.SimplePlane и автоматически разбивает текстуру на девять частей с заданными отступами.

Конструктор

const panel = new PIXI.NineSlicePlane(
    texture,
    leftWidth,
    topHeight,
    rightWidth,
    bottomHeight
);

Параметры:

  • texture — текстура
  • leftWidth — ширина левой фиксированной области
  • topHeight — высота верхней фиксированной области
  • rightWidth — ширина правой фиксированной области
  • bottomHeight — высота нижней фиксированной области

Эти значения определяют размеры нерастягиваемых областей.


Пример создания масштабируемой панели

const app = new PIXI.Application({
    width: 800,
    height: 600
});

document.body.appendChild(app.view);

const texture = PIXI.Texture.from('panel.png');

const panel = new PIXI.NineSlicePlane(
    texture,
    20, // левая граница
    20, // верхняя граница
    20, // правая граница
    20  // нижняя граница
);

panel.width = 400;
panel.height = 300;

panel.position.set(200, 150);

app.stage.addChild(panel);

В этом примере:

  • Углы размером 20×20 сохраняются без изменений
  • Грани растягиваются корректно
  • Центр масштабируется свободно

Подготовка текстуры для 9-slice

Корректная работа метода напрямую зависит от подготовки графики.

Требования к изображению

  1. Углы должны содержать неизменяемые декоративные элементы
  2. Грани должны быть предназначены для растяжения
  3. Центр — однотонный или текстурно нейтральный участок

Если границы заданы неверно:

  • углы могут частично растягиваться
  • на гранях появятся артефакты
  • визуальная структура нарушится

Оптимальная структура кнопки

  • Толщина рамки — фиксированная (например, 12–24 px)
  • Центр — однотонная заливка или простая текстура
  • Без сложных узоров на растягиваемых областях

Динамическое изменение размеров

NineSlicePlane позволяет изменять размеры в реальном времени:

panel.width = 600;
panel.height = 120;

Перерасчёт происходит автоматически.

Это удобно для:

  • адаптивных интерфейсов
  • всплывающих окон
  • масштабируемых HUD-панелей
  • прогресс-баров

Изменение границ после создания

Границы можно изменять динамически:

panel.leftWidth = 30;
panel.rightWidth = 30;
panel.topHeight = 15;
panel.bottomHeight = 15;

После изменения происходит перерасчёт геометрии.

Важно учитывать:

  • Сумма leftWidth + rightWidth не должна превышать ширину текстуры
  • Сумма topHeight + bottomHeight не должна превышать высоту текстуры

Внутренний механизм работы

NineSlicePlane создаёт сетку из 4×4 вершин (16 точек), формируя 9 прямоугольных областей.

При изменении размеров:

  1. Пересчитываются позиции внутренних вершин
  2. Угловые координаты остаются фиксированными
  3. Центральная область масштабируется пропорционально

Фактически используется модифицированная сетка (mesh), а не девять отдельных спрайтов. Это повышает производительность по сравнению с ручной реализацией через контейнер из 9 объектов.


Производительность

Преимущества использования NineSlicePlane:

  • Один draw call
  • Одна текстура
  • Минимальные накладные расходы

Ручная реализация через 9 спрайтов:

  • 9 отдельных объектов
  • Больше операций обновления
  • Более сложная логика позиционирования

При большом количестве UI-элементов это становится критичным.


Практические применения

Кнопки

const button = new PIXI.NineSlicePlane(texture, 16, 16, 16, 16);
button.width = 200;
button.height = 60;

Окна интерфейса

  • Фиксированные декоративные углы
  • Растягиваемые рамки
  • Центр под контент

Прогресс-бары

  • Центральная область масштабируется по ширине
  • Высота остаётся фиксированной
  • Углы сохраняют форму

Ограничения метода

  1. Подходит только для прямоугольных объектов
  2. Не поддерживает произвольные формы
  3. Не подходит для сложных текстур с мелкими деталями на гранях
  4. Не решает проблему искажения при повороте

Для нестандартных форм требуется использование Mesh или пользовательских шейдеров.


Сравнение с обычным масштабированием

Подход Результат
sprite.scale.set(2) Искажение всех элементов
sprite.width/height Пропорциональное растяжение
NineSlicePlane Корректное масштабирование рамок

Метод 9-slice — стандарт индустрии для UI-графики.


Типичные ошибки

Неверные размеры границ

Если границы заданы меньше фактической толщины рамки:

  • часть угла попадёт в растягиваемую область

Использование текстур с прозрачными отступами

Это может привести к визуальному «пустому» пространству по краям.

Слишком маленькая центральная область

Если центр слишком мал, при масштабировании появится сильное растяжение текстуры.


Рекомендации по организации проекта

  • Хранить UI-текстуры в отдельном атласе
  • Использовать единый стандарт толщины рамок
  • Проверять корректность 9-slice на минимальном и максимальном размере
  • Не масштабировать объект через scale, использовать width и height

Расширенные техники

Комбинирование с текстом

const label = new PIXI.Text('Start', style);
label.anchor.set(0.5);
label.position.set(button.width / 2, button.height / 2);

button.addChild(label);

Текст центрируется внутри масштабируемой кнопки.

Анимация размеров

app.ticker.add(() => {
    panel.width = 300 + Math.sin(Date.now() * 0.005) * 50;
});

NineSlicePlane корректно пересчитывает геометрию при каждом кадре.


Архитектурная роль 9-slice в UI-системах

В крупных проектах 9-slice используется как базовый строительный элемент:

  • базовый класс для кнопок
  • панели контейнеров
  • модальные окна
  • диалоговые блоки

Поверх него строится логика состояний (hover, active, disabled) через замену текстур или использование спрайт-листов.


Метод 9-slice в PixiJS является фундаментальным инструментом создания гибких, адаптивных и производительных интерфейсов. Правильная подготовка текстур и корректная настройка границ позволяют создавать UI-элементы произвольного размера без визуальных искажений и с минимальными затратами производительности.