Демо + код
ИМХО, это довольно сложная анимация, я использовал много трюков, которые я бы не использовал обычным образом, возможно, их решение было бы гораздо проще.
Я считал, что высота предметов является динамической, что было самой большой проблемой, но это делает анимацию очень гибкой.
Поскольку у нас есть динамические значения, мы будем использовать AnimationBuilder of Angular.
При использовании AnimationBuilder у нас нет доступа к запросам :enter
и :leave
, поэтому оба элемента должны отображаться постоянно.
Давайте начнем с простой части, CSS, которая стала намного проще:
#holder {
position: relative;
overflow: hidden;
}
#show {
background-color: darkcyan;
}
#edit {
background-color: cornflowerblue;
visibility: hidden; <-- we start with the 'edit' panel hidden
position: absolute; <-- we start with the 'edit' panel hidden
}
Шаблон:
<div #refHolder id="holder">
<div #refShow id="show">
<div>show</div>
<div>show</div>
<div>show</div>
</div>
<div #refEdit id="edit">
<div>edit</div>
<div>edit</div>
<div>edit</div>
<div>edit</div>
<div>edit</div>
<div>edit</div>
</div>
</div>
<button (click)="toggleState()">Toggle</button>
Здесь обратите внимание на ссылки на ссылки на 3 элемента (#refHolder, #refShow, #refEdit), которые мы будем использовать для доступа к их свойствам при создании анимации.
Обратите внимание также на идентификаторы, которые используются как для стилизации, так и для запросов во время анимации.
TS:
Нам нужно определить набор свойств
state = true;
@ViewChild('refHolder') refHolder: ElementRef;
@ViewChild('refShow') refShow: ElementRef;
@ViewChild('refEdit') refEdit: ElementRef;
private animationFactory: AnimationFactory;
Добавление службы AnimationBuilder в конструктор:
constructor(
private animationBuilder: AnimationBuilder,
) { }
Давайте закодируем функцию переключения, которая сначала будет переворачивать состояние, а затем создавать и воспроизводить анимацию.
toggleState() {
this.state = !this.state;
this.buildAndPlayAnimation();
}
А теперь давайте выполним сложную часть, построим анимацию:
private buildAndPlayAnimation() {
let animationFactory: AnimationFactory;
animationFactory = this.animationBuilder.build([
group([
query(this.state ? '#edit' : '#show', [
style({
position: 'absolute',
opacity: 1,
top: 0,
right: 0,
left: 0,
height: Math.max(this.state ? this.refShow.nativeElement.clientHeight : this.refEdit.nativeElement.clientHeight, this.refHolder.nativeElement.clientHeight),
}),
animate('.5s', style({
opacity: 0,
visibility: 'hidden',
})),
style({
top: 'auto',
bottom: 'auto',
right: 'auto',
height: '*',
})
]),
query(this.state ? '#show' : '#edit', [
style({
position: 'static',
opacity: 0,
height: this.refHolder.nativeElement.clientHeight,
visibility: 'visible',
}),
animate('.5s', style({
opacity: 1,
height: '*',
})),
])
])
]);
animationFactory.create(this.refHolder.nativeElement).play();
}
По сути, я использовал троичные операторы, чтобы не создавать дважды анимацию, потому что она должна работать в обоих направлениях. Элементы установлены как невидимые и позиционируются как абсолютные, чтобы не препятствовать взаимодействию пользователя с видимой панелью.