Инвертирование масок

Маскирование в Two.js основано на использовании одного объекта в качестве области видимости для другого. Любая фигура или группа может выступать маской, ограничивая отображение дочернего элемента только той областью, где маска «прозрачна».

Ключевые особенности:

  • Маска применяется через свойство mask
  • Маской может быть любая фигура или группа (Two.Path, Two.Group, Two.Circle и др.)
  • Маска не отображается напрямую, а влияет на визуализацию связанного объекта
  • Координаты маски и маскируемого объекта синхронизируются через их трансформации

Пример базового применения:

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

const rect = two.makeRectangle(250, 250, 300, 300);
rect.fill = 'blue';

const circleMask = two.makeCircle(250, 250, 100);

rect.mask = circleMask;

two.update();

В этом примере прямоугольник отображается только внутри круга.


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

Стандартная маска в Two.js работает по принципу «показать внутри формы». Это означает:

  • Видна только внутренняя область маски
  • Всё вне маски полностью скрыто

Иногда требуется противоположное поведение:

  • Скрыть внутреннюю область
  • Показать всё, что находится снаружи маски

Такой эффект называется инвертированием маски.


Концепция инвертированной маски

Инвертированная маска — это логическое отрицание стандартной маски:

Тип маски Что видно
Обычная Внутри формы
Инвертированная Вне формы

В Two.js нет встроенного свойства вроде invertMask = true, поэтому инвертирование достигается через композицию объектов.


Подходы к инвертированию масок

1. Использование большой фоновой формы

Создаётся большая фигура (обычно прямоугольник), покрывающая всю сцену, и из неё «вырезается» область маски.

Идея:

  • Большой прямоугольник = видимая область
  • Вырезанная фигура = «дыра» (инверсия)

Пример:

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

// Основной объект
const rect = two.makeRectangle(250, 250, 300, 300);
rect.fill = 'green';

// Большая маска
const fullMask = two.makeRectangle(250, 250, 500, 500);

// Вырезаемая область
const hole = two.makeCircle(250, 250, 100);

// Группа маски
const maskGroup = two.makeGroup(fullMask, hole);

// Настройка режима объединения
maskGroup.subtractions = [hole];

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

two.update();

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

  • Используется свойство subtractions
  • Требуется поддержка boolean-операций (в некоторых версиях Two.js ограничена)

2. Композиция через CanvasRenderer

При использовании CanvasRenderer возможно ручное управление композитингом через глобальные операции наложения (globalCompositeOperation).

Принцип:

  • Сначала рисуется объект
  • Затем применяется маска с режимом destination-out

Пример логики:

ctx.globalCompositeOperation = 'destination-out';

Это приводит к «вырезанию» формы из уже нарисованного изображения.

Ограничения:

  • Требуется доступ к внутреннему canvas-контексту
  • Нарушает абстракцию Two.js
  • Может конфликтовать с автоматическим рендерингом

3. Использование SVG renderer и clipPath

При использовании SVGRenderer Two.js генерирует <clipPath>. Инверсия может быть реализована через:

  • Дополнительные SVG-операции
  • Использование mask вместо clipPath

Идея:

  • Создать SVG <mask>
  • Белое = видно, чёрное = скрыто
  • Инверсия достигается сменой цветов

Однако:

  • Two.js напрямую не предоставляет API для управления этим уровнем
  • Требуется модификация DOM после рендера

Практическая реализация через двойную маску

Один из наиболее универсальных способов — комбинирование двух масок:

  1. Основная фигура (что показывать)
  2. Инвертирующая фигура (что скрывать)

Шаги:

  1. Создать группу, представляющую весь экран

  2. Добавить в неё:

    • фон
    • вырезаемую фигуру
  3. Назначить группу как маску

Пример:

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

// Контент
const content = two.makeRectangle(300, 200, 400, 300);
content.fill = 'orange';

// Полная область
const full = two.makeRectangle(300, 200, 600, 400);
full.fill = 'white';

// Отверстие
const hole = two.makeCircle(300, 200, 80);
hole.fill = 'black';

// Группа маски
const mask = two.makeGroup(full, hole);

// Назначение
content.mask = mask;

two.update();

Динамическая инверсия маски

Инвертированные маски особенно полезны при анимации:

  • «Прожектор» (spotlight)
  • Область внимания
  • Скрытие/раскрытие UI

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

two.bind('update', function(frameCount) {
  hole.translation.x = 300 + Math.sin(frameCount * 0.05) * 100;
});

Эффект:

  • Отверстие движется
  • Вся сцена затемнена, кроме области круга

Производительность и оптимизация

Инвертированные маски могут быть затратными:

Причины:

  • Дополнительные группы
  • Булевы операции
  • Перерисовка сложных форм

Рекомендации:

  • Использовать простые геометрические фигуры
  • Минимизировать вложенность групп
  • Избегать частых изменений структуры маски
  • Кэшировать неизменяемые элементы

Частые проблемы

Маска не работает

Причины:

  • Маска не добавлена в сцену
  • Неправильный порядок объектов
  • Ошибка в группировке

Инверсия не даёт ожидаемого результата

Причины:

  • Нет поддержки boolean-операций
  • Неправильные fill-цвета (в SVG-режиме)
  • Используется clipPath вместо mask

Артефакты при анимации

Причины:

  • Сложные path-объекты
  • Частое изменение геометрии

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

Использование нескольких отверстий

const hole1 = two.makeCircle(200, 200, 50);
const hole2 = two.makeCircle(400, 200, 50);

maskGroup.add(hole1, hole2);
maskGroup.subtractions = [hole1, hole2];

Инверсия сложных форм

Можно использовать Two.Path:

const path = new Two.Path([
  new Two.Anchor(100, 100),
  new Two.Anchor(200, 100),
  new Two.Anchor(200, 200),
  new Two.Anchor(100, 200)
], true);

Сравнение подходов

Метод Простота Гибкость Производительность
Большая форма + вырез Средняя Высокая Средняя
Canvas compositing Низкая Очень высокая Высокая
SVG mask Низкая Очень высокая Высокая

Архитектурные рекомендации

  • Выделять маски в отдельные модули
  • Использовать фабрики для создания масок
  • Избегать смешивания логики анимации и маскирования
  • Тестировать отдельно в Canvas и SVG режимах

Взаимодействие с трансформациями

Маска наследует трансформации:

  • translation
  • rotation
  • scale

Важно:

  • Маска и объект должны быть в одной системе координат
  • При вложенности групп трансформации суммируются

Комбинирование с прозрачностью

Инвертированные маски хорошо сочетаются с альфа-каналом:

full.opacity = 0.8;

Эффект:

  • Затемнение всей сцены
  • Яркое «окно» в области отверстия

Использование в интерфейсах

Типичные сценарии:

  • Подсветка элементов интерфейса
  • Обучающие подсказки (tutorial overlay)
  • Фокусировка внимания пользователя
  • Эффекты переходов

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