Сложные маски из нескольких объектов

Механизм масок в Two.js основан на использовании любого графического объекта (или группы объектов) в качестве области видимости для другого элемента. Маска определяет, какие части целевого объекта будут видимы, а какие — скрыты. В отличие от простых масок, где используется один примитив (например, круг или прямоугольник), сложные маски формируются из нескольких объектов, объединённых в группу.

Ключевая особенность: маска учитывает суммарную геометрию всех вложенных элементов, включая их трансформации, порядок наложения и свойства прозрачности.


Создание сложной маски через группу

Two.js предоставляет возможность объединять объекты в группу (Two.Group), которая затем может быть использована как маска.

const two = new Two({ width: 500, height: 500 }).appendTo(document.body);

// Создание элементов маски
const circle = two.makeCircle(200, 200, 100);
const rect = two.makeRectangle(300, 200, 150, 150);

// Группировка
const maskGroup = two.makeGroup(circle, rect);

// Целевой объект
const image = two.makeRectangle(250, 250, 400, 400);
image.fill = '#3498db';

// Применение маски
image.mask = maskGroup;

two.update();

В данном примере маска формируется объединением круга и прямоугольника. Видимой остаётся только та часть image, которая пересекается с объединённой областью этих фигур.


Логика объединения объектов в маске

При использовании нескольких объектов внутри маски действует правило:

  • Области суммируются (логическое OR) — если хотя бы один объект маски покрывает пиксель, он остаётся видимым
  • Перекрытия усиливают эффект, но не изменяют прозрачность (если не используется альфа-канал)

Влияние прозрачности

Если у элементов маски заданы значения opacity или используются полупрозрачные цвета:

circle.opacity = 0.5;
rect.opacity = 0.8;

то итоговая маска может работать как градиентная область видимости, особенно при использовании CanvasRenderer.


Вложенные группы в масках

Two.js поддерживает вложенные группы, что позволяет создавать иерархически сложные маски:

const smallCircle = two.makeCircle(150, 150, 50);
const bigCircle = two.makeCircle(350, 350, 100);

const subGroup = two.makeGroup(smallCircle);
const mainGroup = two.makeGroup(subGroup, bigCircle);

image.mask = mainGroup;

Особенности:

  • Все трансформации родительских групп применяются к дочерним элементам
  • Вложенность влияет на итоговую форму маски
  • Можно комбинировать разные уровни детализации

Трансформации маски

Маска — это полноценный объект сцены, поэтому к ней применимы все трансформации:

Перемещение

maskGroup.translation.set(50, 0);

Масштабирование

maskGroup.scale = 1.5;

Вращение

maskGroup.rotation = Math.PI / 4;

Все изменения немедленно влияют на область видимости целевого объекта.


Динамические маски

Сложные маски часто используются в анимации. Two.js позволяет изменять структуру маски в реальном времени.

Пример анимации:

two.bind('update', function(frameCount) {
  circle.radius = 100 + Math.sin(frameCount * 0.05) * 50;
  rect.rotation += 0.02;
});

Результат:

  • Маска постоянно изменяется
  • Видимая область «оживает»
  • Создаётся эффект динамического окна

Маски с использованием кривых и произвольных форм

Помимо базовых фигур, маска может включать сложные пути (Two.Path):

const path = two.makePath(
  100, 100,
  200, 50,
  300, 150,
  true
);

path.fill = '#000';

const maskGroup = two.makeGroup(path, circle);
image.mask = maskGroup;

Преимущества:

  • Возможность создания нестандартных форм
  • Поддержка кривых Безье
  • Точная настройка силуэта маски

Порядок отрисовки внутри маски

Хотя маска учитывает все элементы, порядок (z-index) может влиять при использовании прозрачности и сложных наложений:

maskGroup.add(rect, circle);
  • Последний добавленный элемент может визуально «перекрывать» предыдущий
  • Особенно заметно при использовании альфа-канала

Использование отрицательных пространств (инверсия)

Two.js напрямую не поддерживает инверсию маски (вырезание области), но её можно имитировать:

Подход:

  1. Создать большой фон (например, прямоугольник на весь экран)
  2. Добавить внутрь «вырезающие» формы с прозрачностью
const full = two.makeRectangle(250, 250, 500, 500);
full.fill = '#000';

const hole = two.makeCircle(250, 250, 100);
hole.fill = 'transparent';

const maskGroup = two.makeGroup(full, hole);
image.mask = maskGroup;

Результат зависит от рендерера и может требовать дополнительных настроек.


Ограничения и особенности

Canvas vs SVG

  • В CanvasRenderer маски работают быстрее, но могут иметь ограничения по качеству

  • В SVGRenderer:

    • Более точная геометрия
    • Поддержка сложных прозрачностей
    • Возможны особенности браузеров

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

Сложные маски из множества объектов:

  • увеличивают нагрузку на отрисовку
  • требуют оптимизации (например, уменьшение количества точек в путях)

Практические паттерны

1. Маска-«окно» из нескольких фигур

Комбинация кругов для создания эффекта множественных отверстий:

const holes = [];

for (let i = 0; i < 5; i++) {
  holes.push(two.makeCircle(100 + i * 80, 250, 40));
}

const maskGroup = two.makeGroup(...holes);
image.mask = maskGroup;

2. Комбинированные геометрии

Использование разных типов фигур:

  • круги + прямоугольники
  • пути + линии
  • текст как маска (если преобразован в path)

3. Анимированные сложные маски

Комбинация:

  • изменения радиуса
  • вращения
  • перемещения

создаёт эффект «живой» маски, применяемый в:

  • интерактивных интерфейсах
  • визуализациях
  • играх

Взаимодействие масок с другими свойствами

Прозрачность целевого объекта

image.opacity = 0.7;

Работает независимо от маски, но визуально смешивается с ней.

Обводка (stroke)

Обводка не учитывается в маске, если не преобразована в заливку.


Отладка сложных масок

При работе с несколькими объектами внутри маски полезно:

  • временно отключать маску:

    image.mask = null;
  • изменять цвет элементов маски:

    circle.fill = 'red';
  • проверять трансформации групп


Архитектура сложных масок

При построении масштабных сцен рекомендуется:

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

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

Маска внутри маски

Two.js допускает использование маски у объекта, который сам находится в маске:

inner.mask = anotherMask;
outer.mask = inner;

Это позволяет создавать:

  • каскадные эффекты
  • сложные переходы
  • многослойные визуализации

Генерация масок на лету

Создание объектов маски динамически:

function createRandomMask() {
  const shapes = [];
  for (let i = 0; i < 10; i++) {
    shapes.push(two.makeCircle(
      Math.random() * 500,
      Math.random() * 500,
      Math.random() * 50
    ));
  }
  return two.makeGroup(...shapes);
}

image.mask = createRandomMask();

Сложные маски в Two.js представляют собой мощный инструмент композиции, позволяющий управлять видимостью объектов на уровне геометрии и анимации. Их использование открывает возможности для создания выразительных визуальных эффектов, выходящих за рамки простых форм и статичных сцен.