Вложенные группы

В библиотеке SVG.js группировка элементов реализуется через объект G (group), позволяющий объединять несколько SVG-элементов в логическую структуру. Вложенные группы — это группы, которые содержат внутри себя другие группы, формируя иерархию.

Такой подход позволяет:

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

Иерархия групп аналогична DOM-дереву: каждая группа может иметь родителя и потомков.


Создание вложенных групп

Базовое создание группы выполняется методом group():

const draw = SVG().addTo('#app').size(400, 400)

const parentGroup = draw.group()
const childGroup = parentGroup.group()

childGroup.rect(100, 100).fill('red')

В данном примере:

  • parentGroup — корневая группа
  • childGroup — вложенная группа внутри parentGroup
  • прямоугольник принадлежит childGroup, а значит косвенно и parentGroup

Иерархическая структура

Каждый элемент знает своего родителя:

childGroup.parent() === parentGroup // true

Также можно получить всех потомков:

parentGroup.children()

Глубокая вложенность не ограничена:

const g1 = draw.group()
const g2 = g1.group()
const g3 = g2.group()

g3.circle(50)

Иерархия:

g1
 └── g2
      └── g3
           └── circle

Трансформации во вложенных группах

Одно из ключевых преимуществ вложенных групп — каскадное применение трансформаций.

parentGroup.translate(50, 50)
childGroup.rotate(45)

childGroup.rect(100, 100)

Результат:

  • сначала применяется rotate(45) к элементу
  • затем translate(50, 50) от родителя

Порядок трансформаций соответствует иерархии: трансформации родителя применяются после трансформаций потомка.


Наследование атрибутов

Некоторые атрибуты могут наследоваться:

parentGroup.fill('blue')
childGroup.rect(100, 100)

Прямоугольник будет синим, если у него не задан собственный fill.

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


Управление видимостью

Скрытие группы влияет на все вложенные элементы:

parentGroup.hide()

Все дочерние группы и элементы также станут невидимыми.

Показ:

parentGroup.show()

События и вложенность

События могут “всплывать” по иерархии:

childGroup.on('click', () => console.log('child'))
parentGroup.on('click', () => console.log('parent'))

При клике по элементу внутри childGroup:

  1. сработает обработчик childGroup
  2. затем обработчик parentGroup

Это поведение аналогично DOM event bubbling.


Перемещение элементов между группами

Элемент можно переместить в другую группу:

const rect = childGroup.rect(100, 100)

parentGroup.add(rect)

Теперь:

  • элемент больше не принадлежит childGroup
  • он становится прямым потомком parentGroup

Использование add() и put()

Методы отличаются:

  • add() — добавляет существующий элемент
  • put() — аналогично, но возвращает сам элемент
const rect = draw.rect(50, 50)

childGroup.add(rect)

Работа с индексами и порядком

Порядок элементов внутри группы влияет на отрисовку (z-index):

childGroup.front()
childGroup.back()

Для вложенных групп:

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

Удаление групп

Удаление родительской группы удаляет всех потомков:

parentGroup.remove()

Удаление вложенной группы:

childGroup.remove()

Родитель останется, но дочерняя структура исчезнет.


Клонирование вложенных групп

Клонирование сохраняет структуру:

const clone = parentGroup.clone()
draw.add(clone)

Клон содержит:

  • все вложенные группы
  • все элементы
  • текущие атрибуты и трансформации

Практический пример: сложная композиция

const draw = SVG().addTo('#app').size(500, 500)

const scene = draw.group()

const house = scene.group()
const roof = house.group()
const windows = house.group()

house.rect(200, 150).move(100, 200).fill('#ccc')

roof.polygon('100,200 200,100 300,200').fill('#900')

windows.rect(30, 30).move(130, 230).fill('#fff')
windows.rect(30, 30).move(240, 230).fill('#fff')

Структура:

scene
 └── house
      ├── roof
      └── windows

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

  • можно перемещать весь дом (house.translate(...))
  • можно отдельно управлять крышей или окнами
  • логическое разделение компонентов

Масштабирование вложенных групп

house.scale(1.5)

Все элементы внутри house увеличатся пропорционально, включая:

  • размеры
  • позиции
  • вложенные группы

Работа с матрицами преобразований

SVG.js использует матрицы трансформаций. Для вложенных групп итоговая матрица вычисляется как произведение всех матриц предков.

childGroup.transform({ scale: 2 })
parentGroup.transform({ rotate: 30 })

Итоговое преобразование элемента:

Matrix_final = Matrix_parent × Matrix_child

Изоляция логики через вложенные группы

Вложенные группы позволяют разделять:

  • визуальные компоненты
  • интерактивные зоны
  • анимации

Пример:

const ui = draw.group()
const button = ui.group()
const icon = button.group()

button.rect(100, 50).fill('blue')
icon.circle(20).center(50, 25)

Теперь можно:

  • анимировать button
  • отдельно менять icon
  • перемещать весь UI

Анимация вложенных групп

Анимации применяются независимо:

parentGroup.animate(1000).move(200, 200)
childGroup.animate(1000).rotate(360)

Итог:

  • группа перемещается
  • вложенная группа вращается внутри неё

Использование each() для обхода

parentGroup.each(function(i, children) {
  console.log(this)
}, true)

Параметр true означает глубокий обход (включая вложенные группы).


Декомпозиция сложных сцен

При работе с крупной графикой рекомендуется:

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

Пример структуры:

root
 ├── background
 ├── characters
 │    ├── hero
 │    └── enemies
 └── ui

Частые ошибки

1. Потеря контекста при перемещении

Перемещение элемента между группами может изменить его визуальное положение из-за разных трансформаций.

2. Неправильный порядок трансформаций

Вложенность влияет на итоговую матрицу — порядок критичен.

3. Чрезмерная вложенность

Слишком глубокая иерархия усложняет:

  • отладку
  • управление
  • производительность

Оптимизация структуры

Рекомендуется:

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

Взаимодействие с DOM

Каждая группа — это <g> элемент SVG:

<g>
  <g>
    <rect />
  </g>
</g>

SVG.js управляет этой структурой, но понимание DOM помогает при отладке.


Итоговая модель работы

Вложенные группы в SVG.js — это:

  • дерево объектов
  • система трансформаций
  • механизм композиции

Они позволяют строить сложные сцены, управлять ими на разных уровнях и создавать масштабируемую архитектуру SVG-графики.