Использование Velocity.js в экосистеме Angular требует понимания принципов работы фреймворка: компонентной архитектуры, механизма обнаружения изменений (change detection) и управления жизненным циклом. Основная задача — корректно встроить анимации без нарушения реактивной модели Angular.
Velocity.js работает напрямую с DOM, тогда как Angular стремится абстрагировать доступ к нему. Поэтому интеграция должна учитывать баланс между производительностью и архитектурной чистотой.
Velocity.js можно установить через npm:
npm install velocity-animate
Импорт в Angular-компоненте:
import Velocity from 'velocity-animate';
При использовании TypeScript возможны ситуации, когда типы отсутствуют. В этом случае либо устанавливаются сторонние типы, либо объявляется модуль вручную:
declare module 'velocity-animate';
Angular не рекомендует прямой доступ к DOM, но для анимаций это
допустимо через ElementRef:
import { Component, ElementRef, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-box',
template: `<div class="box"></div>`
})
export class BoxComponent implements AfterViewInit {
constructor(private el: ElementRef) {}
ngAfterViewInit() {
const element = this.el.nativeElement.querySelector('.box');
Velocity(element, { opacity: 1, translateY: 0 }, { duration: 500 });
}
}
Ключевой момент — выполнение анимации в ngAfterViewInit,
когда DOM уже доступен.
Для более безопасной работы с DOM применяется Renderer2.
Однако Velocity.js требует прямой ссылки на элемент, поэтому Renderer
используется только для получения элемента:
import { Renderer2, ElementRef } from '@angular/core';
constructor(private renderer: Renderer2, private el: ElementRef) {}
animate() {
const element = this.el.nativeElement.querySelector('.box');
Velocity(element, { scale: 1.2 });
}
Повторное использование логики удобно реализовать через директивы.
import { Directive, ElementRef, Input, OnInit } from '@angular/core';
import Velocity from 'velocity-animate';
@Directive({
selector: '[appVelocity]'
})
export class VelocityDirective implements OnInit {
@Input() animation: any;
@Input() options: any;
constructor(private el: ElementRef) {}
ngOnInit() {
Velocity(this.el.nativeElement, this.animation, this.options);
}
}
Использование в шаблоне:
<div
appVelocity
[animation]="{ opacity: 1, translateX: 100 }"
[options]="{ duration: 300 }">
</div>
Такой подход позволяет декларативно описывать анимации прямо в шаблоне.
Анимации должны учитывать жизненный цикл компонентов:
ngOnInit — инициализация данныхngAfterViewInit — доступ к DOMngOnDestroy — очисткаПример остановки анимации при уничтожении компонента:
import { OnDestroy } from '@angular/core';
export class BoxComponent implements OnDestroy {
element: any;
ngAfterViewInit() {
this.element = document.querySelector('.box');
Velocity(this.element, { opacity: 1 });
}
ngOnDestroy() {
Velocity(this.element, 'stop');
}
}
Velocity.js поддерживает callback-функции, которые можно использовать вместе с Angular-событиями:
Velocity(element, { opacity: 0 }, {
duration: 500,
complete: () => {
this.isVisible = false;
}
});
После завершения анимации изменяется состояние компонента, что инициирует обновление шаблона.
Angular использует Zone.js для отслеживания асинхронных операций. Velocity-анимации могут вызывать лишние циклы change detection.
Для оптимизации используется NgZone:
import { NgZone } from '@angular/core';
constructor(private zone: NgZone) {}
runAnimation(element: HTMLElement) {
this.zone.runOutsideAngular(() => {
Velocity(element, { translateX: 200 }, {
complete: () => {
this.zone.run(() => {
this.animationDone = true;
});
}
});
});
}
Преимущества:
Angular имеет встроенную систему анимаций
(@angular/animations). Velocity.js используется в случаях,
когда:
Пример комбинирования:
import { trigger, state, style, transition, animate } from '@angular/animations';
@Component({
animations: [
trigger('fade', [
state('visible', style({ opacity: 1 })),
transition('* => visible', [
animate('300ms')
])
])
]
})
Velocity можно применять для более сложных сценариев, оставляя простые переходы Angular.
Velocity.js поддерживает цепочки анимаций:
Velocity(element, { opacity: 1 }, { duration: 300 })
.then(() => Velocity(element, { translateX: 100 }))
.then(() => Velocity(element, { scale: 1.5 }));
В Angular такие последовательности удобно вызывать в сервисах.
Вынос логики в сервис повышает переиспользуемость:
import { Injectable } from '@angular/core';
import Velocity from 'velocity-animate';
@Injectable({
providedIn: 'root'
})
export class AnimationService {
fadeIn(element: HTMLElement) {
return Velocity(element, { opacity: 1 }, { duration: 300 });
}
slide(element: HTMLElement) {
return Velocity(element, { translateX: 200 });
}
}
Использование:
constructor(private animationService: AnimationService) {}
ngAfterViewInit() {
this.animationService.fadeIn(this.el.nativeElement);
}
При динамическом рендеринге элементов важно учитывать момент их появления:
@ViewChildren('item') items: QueryList<ElementRef>;
ngAfterViewInit() {
this.items.forEach((item, index) => {
Velocity(item.nativeElement, {
opacity: 1,
translateY: 0
}, {
delay: index * 100
});
});
}
Шаблон:
<div *ngFor="let item of list" #item class="list-item">
{{ item }}
</div>
Velocity можно интегрировать с RxJS:
import { from } from 'rxjs';
from(Velocity(element, { opacity: 1 }))
.subscribe(() => {
console.log('Animation complete');
});
Это позволяет включать анимации в реактивные цепочки.
Velocity.js редко выбрасывает ошибки, но при работе с Angular важно учитывать:
Проверка:
if (element) {
Velocity(element, { opacity: 1 });
}
Velocity поддерживает собственные easing:
Velocity(element, { translateX: 100 }, {
easing: [0.17, 0.67, 0.83, 0.67]
});
Это позволяет добиться более плавных и сложных эффектов, чем стандартные Angular-анимации.
Velocity.js поддерживает анимации SVG:
Velocity(svgElement, { strokeDashoffset: 0 }, { duration: 1000 });
SVG часто используется в Angular для визуализации, и Velocity обеспечивает высокую производительность таких анимаций.
Можно хранить состояние анимации в компоненте:
isAnimating = false;
startAnimation(element: HTMLElement) {
this.isAnimating = true;
Velocity(element, { opacity: 1 }, {
complete: () => {
this.isAnimating = false;
}
});
}
Это полезно для блокировки UI или отображения загрузки.
При ленивой загрузке модулей важно убедиться, что Velocity импортирован в нужном модуле:
@NgModule({
imports: [],
declarations: []
})
export class FeatureModule {}
Velocity не требует глобальной регистрации, но его использование должно быть доступно в каждом модуле, где он применяется.
Angular Router позволяет отслеживать смену страниц:
import { Router, NavigationEnd } from '@angular/router';
constructor(private router: Router) {
this.router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
const element = document.querySelector('.page');
Velocity(element, { opacity: 1 });
}
});
}
Velocity-анимации можно отключать в тестах:
Velocity.mock = true;
Это ускоряет выполнение unit-тестов и предотвращает побочные эффекты.
Наиболее устойчивая схема:
Такой подход позволяет использовать Velocity.js в Angular-приложениях без потери производительности и управляемости кода.