Плавная анимация высоты контейнера в кроссфейдной анимации - PullRequest
0 голосов
/ 08 января 2019

У меня проблема с получением желаемого эффекта в угловой анимации. Типичное перекрестное затухание, и прозрачность делает плавное затухание очень хорошим, но высота не анимированная.

что я делаю не так?

  animations: [
    trigger('fadeInOut', [
      transition('* => *', [
        style({ height: '*' }),
        group([
          query(':leave',
            [
              style({ opacity: 1, position: 'absolute', height: '*' }),
              animate('1s', style({ opacity: 0, height: '*' })),
            ],
            { optional: true },
          ),

          query(':enter',
            [
              style({ opacity: 0 }),
              animate('1s', style({ opacity: 1, height: '*' })),
            ],
            { optional: true },
          ),
        ]),
      ]),
    ]),
  ],

Демонстрация в реальном времени: https://angular -crossfade.stackblitz.io

1 Ответ

0 голосов
/ 08 января 2019

Демо + код

ИМХО, это довольно сложная анимация, я использовал много трюков, которые я бы не использовал обычным образом, возможно, их решение было бы гораздо проще.

Я считал, что высота предметов является динамической, что было самой большой проблемой, но это делает анимацию очень гибкой.

Поскольку у нас есть динамические значения, мы будем использовать 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();
}

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

...