如何在 Angular 中有条件地为 :enter 和 :leave 转换设置动画?

How to animate :enter & :leave transitions conditionally in Angular?

我有一个列表,其中的项目有这样的动画:

<li @animation>

这是我的动画触发器:

trigger('animation', [
  transition(':enter', [
    style({ height: '0px', 'padding-top': '0', 'padding-bottom': '0'}),  // initial
    animate('0.5s',
      style({ height: '*', 'padding-top': '*', 'padding-bottom': '*'}))  // final
  ]),
  transition(':leave', [
    style({ height: '*', 'padding-top': '*', 'padding-bottom': '*', opacity: 1}),  // initial
    animate('0.5s',
      style({ height: '0px', 'padding-top': '0', 'padding-bottom': '0', opacity: 0}))  // final
  ])
])

如何有条件地为特定项目打开on/off此动画?实际上我正在寻找某事。像这样:

<li [@animation]=item.isAnimated>

根本不起作用。

不幸的是 Angular 文档中只有一句话:

For elements entering or leaving a page (inserted or removed from the DOM), you can make the animations conditional. For example, use *ngIf with the animation trigger in the HTML template.

但是当我将动画注释与 *ngIf 结合使用时,非动画项目显然根本不会显示:

<li *ngIf="item.isAnimated" @animation>

我想进一步显示所有项目,而不考虑 isAnimated 标志。我只想为特定项目打开 on/off 动画。

澄清一下: Angulars :enter:leave 关键字用于动画组件,如果他们是进入离开dom。听起来很简单,但这正是您的方法和您试图实现的目标的问题。不仅仅是动画,如果 dom 中有一个新元素,您希望它更加自定义,因此您需要自己的状态,可以在列表条目的 ngOnInit 和 ngOnDestroy 中由您自己控制。

方法可能如下:

@Component({
  animations: [
    trigger('animation', [
      state('invisible', style({ height: '0px', 'padding-top': '0', 'padding-bottom': '0'}),
      state('visible', style({ height: '*', 'padding-top': '*', 'padding-bottom': '*'})
      transition('invisible => visible', animate('0.5s'))
      transition('visible => invisible', animate('0.5s'))
    ])
  ],
})

private readonly isAnimated: boolean = false/true //Where ever you get this value.
public animationState: string //Or Enum with visible/invisible.

public ngOnInit(): void {
  if (this.isAnimated) {
    animationState = 'visible'
  }
}

public ngOnDestroy(): void {
  if (this.isAnimated && this.animationState === 'visible') {
    animationState = 'invisible'
  }
}
<li [@animation]="animationState"/>

如果此方法还有任何疑问或问题 - 让我知道,我们可以调整和讨论。

根据Angular IO

当为真时,特殊动画控件绑定@.disabled 绑定会阻止所有动画呈现。将 @.disabled 绑定放在元素上以禁用元素本身的动画,以及元素内的任何内部动画触发器。

以下示例展示了如何使用此功能:

@Component({
    selector: 'my-component',
    template: `
    <div [@.disabled]="isDisabled">
        <div [@childAnimation]="exp"></div>
    </div>
    `,
    animations: [
        trigger("childAnimation", [
            // ...
        ])
    ]
})
class MyComponent {
      isDisabled = true;
  exp = '...';
}

当@.disabled 为真时,它会阻止@childAnimation 触发器以及任何内部动画。

创建一个no-op动画触发器并插入到:enter

之前

我认为 [@.disabled] 方法很好,但创建一些东西并通过不同的机制禁用它感觉很奇怪。幸运的是,还有一种更简单的方法。


首先,请务必记住 :enter 完全 等同于 void => *。事实上,它在 Angular 源代码中。

function parseAnimationAlias(alias: string, errors: string[]): string|TransitionMatcherFn {
  switch (alias) {
    case ':enter':
      return 'void => *';
    case ':leave':
      return '* => void';
    case ':increment':
      return (fromState: any, toState: any): boolean => parseFloat(toState) > parseFloat(fromState);
    case ':decrement':
      return (fromState: any, toState: any): boolean => parseFloat(toState) < parseFloat(fromState);
    default:
      errors.push(`The transition alias value "${alias}" is not supported`);
      return '* => *';
  }
}

最简单的解决方案

了解 Angular 的动画引擎仅匹配 第一个 触发器并运行它也很重要。

因此,您需要做的就是创建一个新触发器并将其插入之前 :enter。这个动画是no-op.

transition('void => false', []),
transition(':enter', [...])

:leave

transition('false => void', []),
transition(':leave', [...])

所以你在模板中的动画变成:

[@animation]="enableAnimations"

当它是 false 时,它将匹配并且不执行任何操作。任何其他值都会匹配 :enter 这当然是 void => *.

这样做的好处是它不会破坏任何已经使用 [@animation] 的东西,因为它仍然像以前一样匹配 void => *

注意:即使在 no-op 动画之后,任何 (done) 处理程序仍会执行 - 所以如果您使用一个处理程序,请确保它在没有动画时执行正确的操作。