Обход дерева элементов

SVG.js предоставляет гибкий и удобный способ работы с элементами SVG через объектную модель. Каждый элемент создаётся с помощью методов типа draw.rect(), draw.circle() или draw.path(). После создания элемент можно присвоить переменной для последующей манипуляции:

const draw = SVG().addTo('body').size(600, 400);
const rect = draw.rect(100, 50).fill('#f06');

Элемент rect теперь является объектом класса SVG.Element и поддерживает методы для изменения атрибутов, трансформаций и взаимодействия с другими элементами.

Навигация по дереву элементов

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

  • children() — возвращает массив прямых дочерних элементов.
  • parent() — возвращает родительский элемент.
  • first() и last() — возвращают первый и последний дочерний элемент.
  • find(selector) — возвращает массив элементов, соответствующих CSS-подобному селектору.
  • each(callback) — выполняет функцию для каждого дочернего элемента.

Пример обхода всех дочерних элементов:

const group = draw.group();
group.rect(50, 50).fill('#0f9');
group.circle(40).fill('#09f');

group.each(el => {
    console.log(el.type); // rect, circle
});

Фильтрация и поиск элементов

Метод find() позволяет искать элементы по тегу, классу или атрибуту. Он поддерживает CSS-подобные селекторы:

draw.rect(50, 50).addClass('highlight');
draw.circle(30).addClass('highlight');

const highlights = draw.find('.highlight');
highlights.each(el => el.fill('#f90'));

Для поиска элементов по атрибуту применяется синтаксис [атрибут="значение"]:

draw.rect(60, 60).attr({dataType: 'button'});

const buttons = draw.find('[dataType="button"]');
buttons.each(el => el.stroke({color: '#000', width: 2}));

Рекурсивный обход дерева

Для работы с глубокими иерархиями удобно использовать рекурсивные функции. Метод children() возвращает прямых потомков, а рекурсия позволяет обрабатывать всех вложенных элементов:

function traverse(element, callback) {
    callback(element);
    element.children().forEach(child => traverse(child, callback));
}

traverse(draw, el => {
    console.log(el.type, el.id());
});

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

Применение фильтров к группе элементов

Фильтры в SVG.js можно применять не только к отдельным объектам, но и к контейнерам, что упрощает массовое оформление элементов:

const group = draw.group();
group.rect(100, 100).fill('#f06');
group.circle(50).fill('#0f9');

group.filterWith(function(add) {
    add.gaussianBlur(5);
});

Метод filterWith создаёт фильтр, который применяется ко всем дочерним элементам, сохраняя структуру дерева.

Отслеживание событий на уровне дерева

SVG.js позволяет навешивать события на отдельные элементы и на группы:

const group = draw.group();
group.rect(80, 80).fill('#ff0');
group.circle(40).fill('#0ff');

group.on('click', e => {
    console.log('Нажатие на группу', e.target.type);
});

События на уровне группы позволяют отслеживать взаимодействия с любым дочерним элементом, не навешивая обработчик на каждый объект отдельно.

Модификация атрибутов всех потомков

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

traverse(group, el => el.fill('#333'));

Можно также комбинировать фильтры и селекторы для избирательной модификации:

traverse(group, el => {
    if (el.type === 'circle') {
        el.fill('#f00');
    }
});

Пример полной структуры с обходом

const draw = SVG().addTo('body').size(800, 600);

const rootGroup = draw.group();
const group1 = rootGroup.group();
const group2 = rootGroup.group();

group1.rect(100, 50).fill('#f06').addClass('interactive');
group1.circle(40).fill('#0f9');
group2.rect(80, 80).fill('#09f');
group2.circle(60).fill('#ff0');

traverse(rootGroup, el => {
    console.log(`Элемент: ${el.type}, Класс: ${el.attr('class')}`);
});

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