Работа с псевдоэлементами

Псевдоэлементы ::before и ::after не являются частью DOM-дерева. Они существуют исключительно на уровне CSS-рендеринга и не представлены в виде отдельных узлов, к которым можно обратиться через JavaScript. Это ключевое ограничение напрямую влияет на возможности библиотеки Velocity.js, поскольку она работает только с реальными DOM-элементами.

Velocity.js анимирует свойства элементов, доступных через HTMLElement.style, а также вычисляемые стили, но не имеет механизма прямого доступа к псевдоэлементам. Следовательно, любые попытки выполнить анимацию через стандартный вызов:

Velocity(element, { opacity: 0.5 })

будут работать только с самим element, но не затронут его ::before или ::after.

Почему псевдоэлементы недоступны для JavaScript-анимации

Псевдоэлементы создаются браузером на этапе рендеринга CSSOM. Они не имеют собственного DOM-узла и не могут быть:

  • выбраны через document.querySelector
  • изменены через style напрямую
  • переданы в Velocity.js как цель анимации

CSS-движок просто интерпретирует их как часть визуального слоя элемента. Поэтому любые изменения возможны только через:

  • изменение CSS-классов родительского элемента
  • изменение CSS-переменных
  • изменение стилей, влияющих на псевдоэлемент косвенно

Косвенное управление через CSS-классы

Наиболее распространённый подход — управление состоянием псевдоэлементов через классы.

CSS:

.button::before {
  content: "";
  position: absolute;
  width: 100%;
  height: 100%;
  background: black;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.button.active::before {
  opacity: 1;
}

JavaScript с Velocity.js:

Velocity(button, { scale: 1.05 }, { duration: 200 });

button.classList.add("active");

Здесь Velocity.js отвечает за анимацию самого элемента, а псевдоэлемент управляется через CSS-переходы. Такой подход разделяет ответственность между инструментами и позволяет сохранить предсказуемость поведения.

Использование CSS-переменных для анимации псевдоэлементов

Более гибкий способ — управление визуальными параметрами через CSS custom properties.

.card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, var(--overlay-opacity, 0));
}

Jav * aScript:

Velocity(card, {
  scale: 1.1
}, {
  duration: 300
});

card.style.setProperty("--overlay-opacity", "0.8");

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

Эмуляция псевдоэлементов через реальные DOM-узлы

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

HTML:

<div class="card">
  <div class="card-before"></div>
</div>

CSS:

.card {
  position: relative;
}

.card-before {
  position: absolute;
  inset: 0;
  background: linear-gradient(45deg, transparent, black);
  opacity: 0;
}

Velocity.js:

Velocity(card.querySelector(".card-before"), {
  opacity: 1,
  translateY: -10
}, {
  duration: 400
});

Такой подход полностью снимает ограничения псевдоэлементов, поскольку теперь анимируемый слой является частью DOM.

Синхронизация анимаций Velocity.js и CSS transitions

При комбинировании Velocity.js и CSS-анимаций важно учитывать порядок обновления рендера. Velocity.js изменяет свойства через requestAnimationFrame, а CSS transitions работают асинхронно.

Пример синхронизации:

Velocity(card, {
  rotateZ: 5
}, {
  duration: 250,
  complete: function () {
    card.classList.add("show-after");
  }
});

CSS:

.card::after {
  opacity: 0;
  transition: opacity 0.2s ease;
}

.card.show-after::after {
  opacity: 1;
}

Использование callback complete позволяет точно определить момент завершения анимации Velocity.js и только затем запускать CSS-изменения псевдоэлементов.

Работа через box-shadow как альтернатива псевдоэлементам

В некоторых случаях псевдоэлемент можно заменить визуальными эффектами через box-shadow, который полностью управляется Velocity.js.

.button {
  box-shadow: 0 0 0 rgba(0, 0, 0, 0);
}
Velocity(button, {
  boxShadowBlur: 20,
  boxShadowSpread: 5
}, {
  duration: 300
});

Хотя это не является прямой заменой ::before или ::after, подобный метод часто используется для создания glow-эффектов без дополнительных DOM-узлов.

Использование SVG как замены псевдоэлементов

SVG предоставляет более гибкую альтернативу псевдоэлементам при необходимости сложной анимации.

HTML:

<div class="icon">
  <svg viewBox="0 0 100 100">
    <rect class="overlay" width="100" height="100"></rect>
  </svg>
</div>

Jav * aScript:

Velocity(document.querySelector(".overlay"), {
  opacity: 1,
  scale: 1.2
}, {
  duration: 300
});

SVG-элементы являются полноценными узлами DOM, поэтому Velocity.js может работать с ними напрямую, включая трансформации и opacity.

Делегирование визуальной логики CSS

Часто наиболее устойчивым решением становится перенос всей логики псевдоэлементов в CSS, оставляя Velocity.js только управление геометрией и движением основного элемента.

.tile::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle, white, transparent);
  transform: scale(0);
  transition: transform 0.4s ease;
}

.tile.is-active::before {
  transform: scale(1);
}
Velocity(tile, {
  translateY: -10
}, {
  duration: 200
});

tile.classList.add("is-active");

Такое разделение позволяет избежать конфликтов между JS-анимацией и CSS-рендерингом, сохраняя производительность и предсказуемость.

Манипуляции через computed styles

Иногда требуется считывать состояние псевдоэлементов для принятия решений в анимации. Хотя напрямую получить доступ нельзя, можно использовать getComputedStyle:

const styles = getComputedStyle(card, "::after");
const opacity = parseFloat(styles.opacity);

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

Переходы между состояниями через классы и Velocity.js

Комбинированный подход часто выглядит следующим образом:

Velocity(element, {
  scale: 0.9
}, {
  duration: 150
});

requestAnimationFrame(() => {
  element.classList.toggle("expanded");
});

CSS управляет псевдоэлементами, Velocity.js — трансформациями. Такой разделённый поток обновлений обеспечивает отсутствие конфликтов между слоями рендеринга.

Практика построения архитектуры анимаций

При работе с псевдоэлементами в проектах, использующих Velocity.js, формируется устойчивый архитектурный паттерн:

  • Velocity.js отвечает за transform и layout-анимации DOM-элементов
  • CSS отвечает за псевдоэлементы и декоративные слои
  • CSS variables обеспечивают связующее звено
  • отдельные DOM-слои используются только при необходимости сложной анимации

Подобное разделение снижает сложность сопровождения и уменьшает вероятность конфликтов между визуальными слоями.