Главная » Статьи » Анимация динамических компонентов в Angular

Анимация динамических компонентов в Angular

Анимация динамических компонентов в Angular

От автора: в этой статье мы рассмотрим два разных метода, с помощью которых реализуется в Angular анимация динамических компонентов.

В первом примере мы используем чистую анимацию CSS. Давайте создадим для демонстрации кнопку добавления.

Начнем с компонента.

@Component({ template: ` <div class="snackbar-container"> {{ content }} <button (click)="close()">&#x2715;</button> </div> `
})
export class SnackbarComponent { @Input() content = ''; constructor(private host: ElementRef<HTMLElement>) { }
}

Реализация компонента проста. Мы ожидаем получить контент в качестве ввода и отобразить его в контейнере. Давайте добавим некоторые стили.

.snackbar-container { position: fixed; animation: snackbarIn 0.5s; ...
} @keyframes snackbarIn { from { transform: translateY(-100px); opacity: 0; } to { ransform: translateY(100px); opacity: 1; }
}

Теперь мы можем продолжить создавать функциональные возможности, которые будут динамически генерировать компонент snack-bar и добавлять его в body.

@Injectable()
export class Snackbar { constructor(private resolver: ComponentFactoryResolver, private injector: Injector, @Inject(DOCUMENT) private document: Document ) { } open(content: string) { const factory = this.resolver.resolveComponentFactory(SnackbarComponent); const componentRef = factory.create(this.injector); componentRef.instance.content = content; componentRef.hostView.detectChanges(); const { nativeElement } = componentRef.location; this.document.body.appendChild(nativeElement); } }

Метод resolveComponentFactory() принимает компонент и возвращает ComponentFactory. Вы можете представить ComponentFactory, как о объект, который знает, как создать компонент. Когда мы получили Factory, мы можем использовать метод create() для создания экземпляра componentRef, передающего текущий инжектор.

КомпонентRef предоставляет ссылку на собственный элемент DOM, который мы затем добавляем в body. На этом этапе мы можем вызвать метод open(). Результат — мы получим рабочий snack-bar с анимацией.

Теперь давайте перейдем к закрытию snack-bar. Во-первых, нам нужно добавить анимацию закрытия.

@keyframes snackbarOut { to { transform: translateY(-100px); opacity: 0; }
}

Мы прослушаем событие клика закрытия и применяем к элементу snack-bar анимацию закрытия.

@Component({ template: ` <div class="snackbar-container"> {{ content }} <button (click)="close()">&#x2715;</button> </div> `
})
export class SnackbarComponent { @Input() content = ''; @Output() afterClose = new EventEmitter(); constructor(private host: ElementRef<HTMLElement>) { } get container(): HTMLElement { return this.host.nativeElement.querySelector('.snackbar-container') as HTMLElement; } close() { this.container.style.animation = 'snackbarOut 0.3s'; } }

На этом этапе мы увидим анимацию закрытия, но компонент все равно будет виден. Ключевым моментом здесь является ожидание окончания анимации, а затем вызов метода componentRef.destroy() и удаление элемента DOM из body.

Для этого мы можем использовать событие браузера animationend — событие animationend происходит, когда анимация CSS завершена.

@Component({ template: ` <div class="snackbar-container" (animationend)="animationDone($event)"> {{ content }} <button (click)="close()">&#x2715;</button> </div> `
})
export class SnackbarComponent { .. @Output() afterClose = new EventEmitter(); animationDone(event: AnimationEvent) { if (event.animationName === 'snackbarOut') { this.afterClose.emit(true); } } ...
}

Одним из свойств события animationend является имя текущей анимации. Мы можем проверить, является ли текущая анимация анимацией snackbarOut. Если это так, мы можем запустить событие afterClose.

Теперь, в методе open() мы можем подписаться на событие afterClose и удалить компонент, но только после окончания анимации.

open(content: string) { ... componentRef.instance.afterClose.subscribe(() => { componentRef.destroy(); this.document.body.removeChild(nativeElement); });
}

Мы также можем модифицировать и сделать компонент автоматически закрывающимся, и принять продолжительность анимации от потребителя.

open(content: string) { ... componentRef.instance.afterClose.subscribe(() => { componentRef.destroy(); this.document.body.removeChild(nativeElement); });
}

ngOnInit() {
const animate = value => this.renderer.setStyle(this.element, ‘animation’, value);

if (this.dismissible) {
animate(snackbarIn 0.5s);
} else {
animate(snackbarIn 0.5s, snackbarOut 0.5s ${this.duration}ms);
}
}

Наш второй пример использует ту же технику, но на этот раз с помощью анимации Angular. Я не буду подробно его описывать, потому что это в основном тот же поток, что и в первом примере.

Речь идет только об изменении состояния анимации в соответствии со статусом компонента, прослушивании события анимации и удалении компонента. Вот полностью рабочий пример:

Автор: Netanel Basal

Источник: https://netbasal.com/

Редакция: Команда webformyself.