Псевдоэлементы ::before и ::after не
являются частью DOM-дерева. Они существуют исключительно на уровне
CSS-рендеринга и не представлены в виде отдельных узлов, к которым можно
обратиться через JavaScript. Это ключевое ограничение напрямую влияет на
возможности библиотеки Velocity.js, поскольку она работает только с
реальными DOM-элементами.
Velocity.js анимирует свойства элементов, доступных через
HTMLElement.style, а также вычисляемые стили, но не имеет
механизма прямого доступа к псевдоэлементам. Следовательно, любые
попытки выполнить анимацию через стандартный вызов:
Velocity(element, { opacity: 0.5 })
будут работать только с самим element, но не затронут
его ::before или ::after.
Псевдоэлементы создаются браузером на этапе рендеринга CSSOM. Они не имеют собственного DOM-узла и не могут быть:
document.querySelectorstyle напрямую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 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 с визуальными изменениями псевдоэлементов без необходимости прямого доступа к ним.
В сценариях, где требуется полноценная анимация псевдоэлемента, используется его замена на отдельный элемент.
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-анимаций важно учитывать порядок
обновления рендера. 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, который полностью управляется
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 предоставляет более гибкую альтернативу псевдоэлементам при необходимости сложной анимации.
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, оставляя 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-рендерингом, сохраняя производительность и предсказуемость.
Иногда требуется считывать состояние псевдоэлементов для принятия
решений в анимации. Хотя напрямую получить доступ нельзя, можно
использовать getComputedStyle:
const styles = getComputedStyle(card, "::after");
const opacity = parseFloat(styles.opacity);
Это значение можно использовать как триггер для дальнейших анимаций Velocity.js, например, изменяя поведение в зависимости от текущего визуального состояния.
Комбинированный подход часто выглядит следующим образом:
Velocity(element, {
scale: 0.9
}, {
duration: 150
});
requestAnimationFrame(() => {
element.classList.toggle("expanded");
});
CSS управляет псевдоэлементами, Velocity.js — трансформациями. Такой разделённый поток обновлений обеспечивает отсутствие конфликтов между слоями рендеринга.
При работе с псевдоэлементами в проектах, использующих Velocity.js, формируется устойчивый архитектурный паттерн:
Подобное разделение снижает сложность сопровождения и уменьшает вероятность конфликтов между визуальными слоями.