Регистрация пользовательских свойств

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

Регистрация свойств осуществляется через внутреннюю систему hooks и Normalizations, которая управляет преобразованием значений между различными форматами.


Архитектура пользовательских свойств

Система обработки свойств в Velocity.js включает несколько уровней:

  • Hooks — позволяют обращаться к составным свойствам (например, transform)
  • Normalizations — управляют тем, как значения читаются, записываются и интерполируются
  • Tweening — отвечает за промежуточные значения при анимации

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

  • способ чтения текущего значения
  • способ записи нового значения
  • правила преобразования данных

Регистрация через Velocity.RegisterEffect (косвенный способ)

Хотя основная цель — эффекты, через них можно внедрять пользовательские свойства:

Velocity.RegisterEffect("custom.fadeAndScale", {
    defaultDuration: 500,
    calls: [
        [{ opacity: 1, scale: 1 }, 1]
    ]
});

Однако этот подход не добавляет новое свойство напрямую, а лишь комбинирует существующие.


Прямая регистрация через Velocity.CSS.Hooks

Для работы с составными свойствами используется API hooks:

Velocity.CSS.Hooks.register("clipTop", "clip", 0);

Здесь:

  • clipTop — новое пользовательское свойство
  • clip — базовое CSS-свойство
  • 0 — индекс значения внутри составного свойства

Пример использования:

Velocity(element, { clipTop: "10px" });

Создание собственных нормализаций

Наиболее гибкий способ — использование механизма Normalizations:

Velocity.CSS.Normalizations.register({
    name: "customProperty",
    
    // Чтение текущего значения
    get: function(element) {
        return element.dataset.custom || 0;
    },

    // Установка значения
    set: function(element, value) {
        element.dataset.custom = value;
    },

    // Интерполяция (необязательно)
    interpolate: function(start, end, percent) {
        return start + (end - start) * percent;
    }
});

После регистрации:

Velocity(element, { customProperty: 100 });

Работа с единицами измерения

Velocity автоматически обрабатывает единицы (px, %, em), но при создании пользовательских свойств важно явно учитывать формат:

get: function(element) {
    return parseFloat(element.style.getPropertyValue("--my-var")) || 0;
},

set: function(element, value) {
    element.style.setProperty("--my-var", value + "px");
}

Поддержка сложных структур данных

Пользовательские свойства могут представлять не только числа, но и структуры:

Velocity.CSS.Normalizations.register({
    name: "rgbColor",

    get: function(element) {
        return [255, 0, 0];
    },

    set: function(element, value) {
        element.style.backgroundColor = `rgb(${value.join(",")})`;
    },

    interpolate: function(start, end, percent) {
        return start.map((s, i) => Math.round(s + (end[i] - s) * percent));
    }
});

Интеграция с transform

Transform-свойства уже разбиты на hooks (translateX, scale, rotate), но можно добавить собственные:

Velocity.CSS.Hooks.register("skewZ", "transform", 5);

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


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

При регистрации пользовательских свойств следует учитывать:

  • минимизацию DOM-операций внутри set
  • отказ от сложных вычислений в interpolate
  • кэширование значений при необходимости

Нежелательно:

set: function(element, value) {
    element.offsetHeight; // форсирует reflow
    element.style.left = value + "px";
}

Обработка начальных значений

Если значение свойства отсутствует, важно задать fallback:

get: function(element) {
    return element._customValue || 0;
}

Или:

get: function(element) {
    return parseFloat(getComputedStyle(element).width) || 0;
}

Совместимость с requestAnimationFrame

Velocity использует собственный цикл анимации, но пользовательские свойства должны быть:

  • детерминированными
  • независимыми от внешнего состояния
  • быстрыми при вычислении

Отладка пользовательских свойств

Для диагностики:

  • проверка вызова get и set
  • логирование промежуточных значений
  • сравнение с нативными CSS-анимациями

Пример:

set: function(element, value) {
    console.log("Setting value:", value);
    element.style.opacity = value;
}

Расширение для SVG

Velocity поддерживает SVG, но пользовательские свойства позволяют управлять атрибутами:

Velocity.CSS.Normalizations.register({
    name: "svgX",

    get: function(element) {
        return element.getAttribute("x") || 0;
    },

    set: function(element, value) {
        element.setAttribute("x", value);
    }
});

Ограничения системы

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

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

Velocity.CSS.Normalizations.register({
    name: "progressValue",

    get: function(element) {
        return element._progress || 0;
    },

    set: function(element, value) {
        element._progress = value;
        element.style.width = value + "%";
    }
});

Использование:

Velocity(progressBar, { progressValue: 100 }, { duration: 1000 });

Связь с системой easing

Пользовательские свойства автоматически поддерживают easing-функции:

Velocity(element, {
    customProperty: 200
}, {
    easing: "easeInOutQuad"
});

Интерполяция будет учитывать выбранную функцию сглаживания.


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

Регистрация свойств может быть вынесена в отдельные модули:

export function registerCustomProps(Velocity) {
    Velocity.CSS.Normalizations.register({
        name: "customScale",
        get: () => 1,
        set: (el, val) => el.style.transform = `scale(${val})`
    });
}

Это позволяет создавать библиотеки расширений для Velocity.


Взаимодействие с CSS-переменными

Современный подход — использование CSS Variables:

Velocity.CSS.Normalizations.register({
    name: "cssVarX",

    get: function(element) {
        return getComputedStyle(element).getPropertyValue("--x") || 0;
    },

    set: function(element, value) {
        element.style.setProperty("--x", value);
    }
});

Контроль над жизненным циклом значения

Пользовательские свойства могут хранить состояние:

set: function(element, value) {
    if (!element._history) element._history = [];
    element._history.push(value);
}

Это открывает возможности для сложных анимационных сценариев.


Расширение возможностей Velocity

Регистрация пользовательских свойств превращает Velocity.js из простой библиотеки анимации в мощный инструмент:

  • поддержка любых типов данных
  • адаптация под нестандартные интерфейсы
  • интеграция с canvas, WebGL, SVG
  • создание DSL для анимаций