Расширение прототипов

Snap.svg предоставляет мощные возможности для работы с векторной графикой, основанной на SVG, и одной из ключевых особенностей библиотеки является возможность расширять прототипы объектов для создания собственных методов и удобных абстракций.

Прототипы элементов Snap

Каждый элемент, создаваемый с помощью Snap.svg, будь то circle, rect, path или text, является объектом с прототипом Element. Этот прототип содержит базовые методы для манипуляции элементами: трансформации, анимации, работы с атрибутами и событиями. Например:

var s = Snap(400, 400);
var circle = s.circle(100, 100, 50);
circle.attr({
    fill: "#f00",
    stroke: "#000",
    strokeWidth: 2
});

В данном примере circle наследует методы из прототипа Element. Методы attr, transform, animate, click и другие доступны всем элементам SVG, создаваемым через Snap.

Добавление методов через Element.prototype

Для расширения функциональности всех элементов можно добавлять методы напрямую в прототип Element. Это позволяет создавать собственные вспомогательные функции, которые будут доступны каждому элементу. Пример:

Snap.Element.prototype.moveTo = function(x, y) {
    this.attr({ cx: x, cy: y });
    return this;
};

var circle = s.circle(50, 50, 20);
circle.moveTo(200, 150);

В этом примере добавлен метод moveTo, который устанавливает новые координаты центра элемента. Метод возвращает this, что позволяет строить цепочки вызовов.

Расширение прототипа с учетом различных типов элементов

Некоторые методы специфичны для определённых типов элементов. Например, cx и cy актуальны для круга, а x и y — для прямоугольника или текста. Можно создавать универсальные методы с проверкой типа элемента:

Snap.Element.prototype.setPosition = function(x, y) {
    if (this.type === "circle" || this.type === "ellipse") {
        this.attr({ cx: x, cy: y });
    } else if (this.type === "rect" || this.type === "text") {
        this.attr({ x: x, y: y });
    }
    return this;
};

var rect = s.rect(10, 10, 50, 50);
rect.setPosition(100, 100);

var text = s.text(0, 0, "Hello");
text.setPosition(200, 200);

Такой подход позволяет создавать методы, которые работают с разными типами SVG-объектов без дублирования кода.

Создание цепочек вызовов (chaining)

Snap.svg поддерживает цепочки вызовов через возвращение объекта this. При расширении прототипов важно соблюдать этот принцип для сохранения гибкости:

Snap.Element.prototype.resize = function(scale) {
    this.transform(`s${scale},${scale}`);
    return this;
};

circle.resize(1.5).attr({ fill: "#00f" }).animate({ r: 80 }, 1000);

Метод resize изменяет масштаб элемента, после чего можно продолжить работу с элементом через стандартные методы Snap.svg.

Работа с групповыми элементами

Группы элементов (g) создаются через метод group и также имеют прототип, поддерживающий расширение. Методы, добавленные к Element.prototype, будут работать и с отдельными элементами в группе, но для удобной работы с группами можно создавать специализированные методы:

Snap.Element.prototype.setGroupOpacity = function(opacity) {
    if (this.type === "g") {
        this.selectAll("*").forEach(el => el.attr({ opacity: opacity }));
    }
    return this;
};

var group = s.group(
    s.circle(50, 50, 30),
    s.rect(40, 40, 20, 20)
);

group.setGroupOpacity(0.5);

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

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

function createButton(x, y, width, height, label) {
    var rect = s.rect(x, y, width, height, 5, 5).attr({ fill: "#ccc", stroke: "#333", strokeWidth: 2 });
    var text = s.text(x + width/2, y + height/2, label).attr({ "text-anchor": "middle", "dominant-baseline": "middle", fill: "#000" });
    var button = s.group(rect, text);

    button.click(function() {
        console.log(`Button ${label} clicked`);
    });

    return button;
}

var btn = createButton(50, 200, 120, 40, "Click Me");

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

Советы по расширению прототипов

  • Избегать конфликтов имён: перед добавлением метода проверять, существует ли уже метод с таким именем.
  • Возвращать this: для поддержки цепочек вызовов.
  • Использовать проверку типа элемента: чтобы методы работали корректно для разных SVG-типов.
  • Группировка методов: для крупных проектов удобно разделять расширения на отдельные модули.

Расширение прототипов в Snap.svg позволяет создавать высокоуровневые абстракции, упрощать повторяющиеся операции и строить мощные визуальные интерфейсы, полностью используя возможности SVG и JavaScript.