Angular动画备忘录

在网上找不到相关信息,所以进行公开。
(只是根据实际行为推测,并没有追踪来源,所以不要轻信,自己进行验证吧)

    1. 查询时,{ optional: true }是实际必需的。

 

    1. 查询时,对于animateChild()的进入和离开指定将不起作用。

 

    1. 如果在状态转换时没有与之匹配的任何过渡效果,则会立即转换到新的状态。

 

    1. void状态将成为原始样式。

 

    1. 作为子元素设置的动画在父元素进入时,如果没有明确调用animateChild(),则会立即转换到新的状态。

 

    1. 作为子元素设置的动画在父元素离开时,除非以组并行执行,否则不会生效。

 

    1. 作为子元素设置的动画在父元素离开时,如果以组并行进行并且没有相应的过渡效果,则会立即转换为void状态即原始样式。

 

    1. “动画中的再次过渡”通常会合并,但在animateChild()中发生时,会导致显示与状态不一致。在动画时间较长时经常发生。

 

    1. 如果从存在显示和状态不一致的状态转换进行动画播放,会出现不适当的播放。

 

    1. 即使在存在显示和状态不一致的状态中,进行(不当播放中的)“动画中的再次过渡”,显示和状态的不一致仍然会持续。

 

    1. 即使两个状态之间没有显示差异,实际上仍执行了动画。在此期间发生动画将被视为“动画中的再次过渡”处理。

 

    典型示例是在“:enter”中进行长时间的animateChild()动画,而显示差异不存在。通过缩短:enter动画的持续时间可以减少其发生。

请给我一个例子。

動畫定義

import { trigger, transition, style, query, group, animate, animateChild, AnimationMetadata, state } from '@angular/animations';

function optionalQuery(selector: string, animation: AnimationMetadata | AnimationMetadata[]) {
  return query(selector, animation, { optional: true });
}

export class AppAnimations {

  /**
   * 典型的なFloatIn
   * @param triggerName 
   * @param enterStart 新しい状態の横初期位置
   * @param leaveDuration 古い状態が消えるまでの動作時間
   * @param enterDelay 新しい状態の動作開始までのディレイ
   * @param enterDuration 新しい状態のディレイ後の動作時間
   * @returns 
   */
  static floatIn(triggerName: string, enterStart: string = '-1%', leaveDuration: string = '100ms', enterDelay: string = '100ms', enterDuration: string = '300ms') {
    return trigger(triggerName, [
      transition('* <=> *', [
        style({ position: 'relative' }),
        optionalQuery(':enter, :leave', [style({ position: 'absolute', top: 0, left: 0, width: '100%' })]),
        optionalQuery(':enter', [style({ left: enterStart, opacity: 0 })]),
        group([
          optionalQuery(':leave', [animate(`${leaveDuration} ease-out`, style({ opacity: 0 }))]),
          optionalQuery(':enter', [animate(`${enterDuration} ${enterDelay} ease-out`, style({ left: '0%', opacity: 1 }))]),
        ]),
        optionalQuery('@*', animateChild()),
      ])
    ]);
  }

  /**
   * 典型的なOpenClose。状態:open, closed
   * 
   * Angular14以降、このアニメーションを実施するとConsoleに警告が出ます。  
   * ('pointer-events'やoverflowの指定は一般的だが、警告が出てしまう)  
   * 詳細は以下のissueを確認してください。  
   * https://github.com/angular/angular/issues/46928  
   * @param triggerName 
   * @param duration 動作時間
   * @param ease '', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'cubic-bezier(x1, y1, x2, y2)'
   * @param enterDuration animateChild()で:enterする際の挙動対策。短い時間を設定することで問題を発生させづらくする
   * @returns 
   */
  static openClose(triggerName: string = 'openClose', duration: string = '300ms', ease: string = 'ease', enterDuration: string = '100ms') {
    return trigger(triggerName, [
      state('open', style({ height: '*', opacity: 1 })),
      state('closed', style({ height: '0px', opacity: 0, 'pointer-events': 'none', padding: 0, overflow: 'hidden' })),
      transition(':enter', [animate(`${enterDuration} ${ease}`)]),
      transition('* <=> *', [animate(`${duration} ${ease}`)]),
    ]);
  }
}

请在使用端(app.component.ts / html)上设置每个页面的路由,例如{ animation: ‘特定的状态名称’ }。

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  animations: [AppAnimations.floatIn('routeAnimations')]
})
export class AppComponent {
...
  getRouteAnimationData() {
    return this.contexts.getContext('primary')?.route?.snapshot?.data?.['animation'];
  }
...
...
<div [@routeAnimations]="getRouteAnimationData()">
  <router-outlet></router-outlet>
</div>

使用侧(网页 ts / html)

@Component({
  selector: 'app-sample-a2',
  templateUrl: './sample-a2.component.html',
  styleUrls: ['./sample-a2.component.scss'],
  animations: [AppAnimations.openClose('slowOpenClose', '2000ms', 'cubic-bezier(0.5, 0, 0.1, 1)'), AppAnimations.openClose('fastOpenClose', '100ms')]
})
export class SampleA2Component {

  isOpen1: boolean = true;
  isOpen2: boolean = false;

...

  changeIsOpen1_click(): void {
    this.isOpen1 = !this.isOpen1;
  }

  changeIsOpen2_click(): void {
    this.isOpen2 = !this.isOpen2;
  }
<div [@slowOpenClose]="isOpen1 ? 'open' : 'closed'">
   <!-- etc. -->
</div>
<div [@fastOpenClose]="isOpen2 ? 'open' : 'closed'">
   <!-- etc. -->
</div>

如果将enterDuration设置为2000ms等较长时间,则似乎会出现8~11的行为。

bannerAds