【问题标题】:Angular 2 - How to set animations on a Directive?Angular 2 - 如何在指令上设置动画?
【发布时间】:2017-03-18 18:23:57
【问题描述】:

我有一个Directive 我可以添加元素来检查相关元素的当前滚动位置。

看起来像这样:

@Directive({
    selector: '[my-scroll-animation]'
})

当位置达到某个阈值时,我希望元素使用动画出现在屏幕上。我知道对于Component,我可以附加animations 属性以及host 属性中的一些设置来激活动画。

我想要这样的东西:

import { myScrollAnimation } from './animations';

@Directive({
    selector: '[my-scroll-animation]'
    animations: [myScrollAnimation] // <- not possible?
})

如何在指令中实现这一点?

使用:Angular 4.0.0-rc.4

【问题讨论】:

  • 是的,指令没有animations,可能是因为每个元素可能有多个指令,并且动画必须以某种方式堆叠。仍然想知道最简洁的方法是什么。
  • 如果您只是想要组件的可重用动画,您可以创建一个animation.ts 文件,并将其作为动画引用到您的组件中。
  • 重点不是分享动画,而是从指令中触发它们。
  • 我相信 Angular 使用 css 动画(过渡、变换等)。由于指令没有模板和样式,您可以实现的最接近的事情可能是更改指令中的类或属性,同时在您的父组件中拥有动画配置。

标签: javascript angular typescript angular4


【解决方案1】:

Angular 4.2 带来了很多动画改进。其中之一是 AnimationBuilder,它允许程序化动画构建。

您只需在指令中注入 AnimationBuilder,即可将任何 AnimationMetadata 转换为工作动画。

@Directive({
  selector: '[zetFadeInOut]',
})
export class FadeInOutDirective {
  player: AnimationPlayer;

  @Input()
  set show(show: boolean) {
    if (this.player) {
      this.player.destroy();
    }

    const metadata = show ? this.fadeIn() : this.fadeOut();

    const factory = this.builder.build(metadata);
    const player = factory.create(this.el.nativeElement);

    player.play();
  }

  constructor(private builder: AnimationBuilder, private el: ElementRef) {}

  private fadeIn(): AnimationMetadata[] {
    return [
      style({ opacity: 0 }),
      animate('400ms ease-in', style({ opacity: 1 })),
    ];
  }

  private fadeOut(): AnimationMetadata[] {
    return [
      style({ opacity: '*' }),
      animate('400ms ease-in', style({ opacity: 0 })),
    ];
  }
}

【讨论】:

  • 您发布的第二个链接是一种新的指令?
  • 你解决了我的一个大问题!!我问了很多关于角度动态动画的问题,几乎没有答案。
  • “新浪潮”链接已损坏,this might be 4.2+ 新闻中的类似替代方案...
  • 它可以与ng-if 一起使用吗?我试过ngAfterInit,它适用于fadeIn,不适用于fadeOut
【解决方案2】:

我根据 this 对 benshabatnoam 的回答中提到的问题的回应使用了一些解决方法。

指令实际上只是没有模板的组件。您可以通过使用属性选择器(例如:[selector])并制作模板:&lt;ng-content&gt;&lt;/ng-content&gt;,来创建与指令相同的组件。

因此,您可以像这样创建一个“foux-directive”组件:

@Component({
    selector: '[fadeInAnimation]',
    animations: [
        trigger('fadeIn', [
             transition(':enter', [
                 style({opacity:'0'}),
                 animate(200, style({opacity:'1'}))
             ])
        ])
    ], 
    template: `<ng-content></ng-content>`,
})
export class FadeInDirective {
    @HostBinding('@fadeIn') trigger = '';
}

并像使用任何指令一样使用它:

<div fadeInAnimation> something I want to animate </div>

【讨论】:

  • 您的解决方案效果很好。谢谢你。但是我不明白你为什么不使用 Derective 装饰器而不是 Compononet
  • 指令不支持动画,所以指令装饰器不起作用
  • 我试过了,它也适用于fadeOut :)
【解决方案3】:

正如我在您问题的评论部分中提到的那样。

您可以在父组件中进行动画配置。 然后该指令在满足阈值时在其宿主上添加一个类。

在你的指令中,你可以有类似的东西:

@Input() classThatTriggersAnimation = "do-animation";
@HostBinding('[@nameOfAnimation]') animationTrigger = ""; 

// when logic is true
this.animationTrigger = this.classThatTriggersAnimation;

【讨论】:

  • 谢谢。这非常有用。但就我而言,我正在开发一个插件,这意味着我无法控制托管模板的父 Component
  • 如果是这种情况,您将不得不创建一个组件而不是一个指令,它可以包含动态内容。您可以查看引导代码,了解它们如何创建模式,您可以在其中传递“内容”。
  • 很好的答案。只是我想应该是@HostBinding('@nameOfAnimation')
【解决方案4】:

我们开始吧。适用于微小的动画。

我直接使用AnimationPlayer + AnimationBuilder => 无需人为地向前+向后切换状态来触发动画。受this snippet 启发并进一步减少。

import { Directive, Input, HostListener } from '@angular/core';
import { ElementRef } from '@angular/core';
import {
    AnimationPlayer,
    AnimationBuilder
} from '@angular/animations';

@Directive({
    selector: '[clickAnimation]',
})
export class ClickAnimationDirective {
    private player: AnimationPlayer;

    @Input() clickAnimation: any;

    @HostListener('click', ['$event'])
    onClick() {
        // sanitize just in case
        if (this.player) { this.player.destroy(); }

        const factory = this.builder.build(this.clickAnimation);
        const player = factory.create(this.el.nativeElement);

        // ensure back-to-original
        player.onDone(() => { player.reset(); });
        player.play();
    }

    constructor(private builder: AnimationBuilder, private el: ElementRef) { }
}

在您的模板中:

<app-button
    [clickAnimation]="trafficLight"
    ...

在你的组件类中:

public trafficLight: AnimationMetadata[] = [
    style({ borderColor: 'red' }),
    animate('400ms ease-in', style({ borderColor: 'yellow' })),
    animate('400ms ease-in', style({ borderColor: 'cyan' })),
    //alternative way to ensure back to original
    // animate('400ms ease-in', style({ borderColor: '*' })), 
];

【讨论】:

  • 这很棒。满足单一职责的需要,不会弄脏组件文件。
【解决方案5】:

这是 Angular 的 git 存储库中的 open issue(截至今天 - 28/08/17)。请投票支持此问题,以便向开发人员施加压力以更快地发布此问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    • 2017-07-04
    • 2017-07-25
    • 1970-01-01
    • 2017-09-03
    • 2019-03-19
    • 2016-09-10
    相关资源
    最近更新 更多