动画是现代web应用设计中的一个重要方面。好的用户界面要能在不同的状态之间进行更平滑的转场。设计良好的动画不但会让ui更有趣,还会让它更容易使用。
angular的动画系统赋予了制作各种动画效果的能力,以构建出与原生CSS动画性能相同的动画。还获得了额外的让动画 逻辑与其他应用代码紧紧集成在一起的能力,这让动画可以被更容易的出发和控制。
准备工作
在应用中添加动画时,需要在根模块中引入一些与动画相关的模块和函数:
1 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; |
在两个状态中转换
构建一个简单的动画,让一个元素用模型驱动的方式在两个状态之间进行转换,动画会被定义在@Component
元数据中。
首先我们引入动画必须的一些符号:
1 | import {animate, state, style, transition, trigger} from '@angular/animations'; |
然后在组件的元数据中定义一个名叫heroState
的动画触发器。它在两个状态active
和inactive
之间进行转换。当按钮处于激活状态时,会变大、变亮:
1 | @Component({ |
然后可以在组件的页面里面通过[@triggerName]
来将上面定义的动画附加到元素上去:
1 | <button [@heroState]="state" (click)="changeState()">test animation</button> |
这里我们通过按钮的点击事件来触发状态的转换,使用state
来记录状态:
1 | state = 'inactive'; |
这样,点击button,就可以实现状态的转换,相应的动画也会应用上去。
状态与转换
angular动画是由状态和状态之间的转换效果所定义的。
定义状态
动画状态是一个由程序代码定义的字符串的值,上面的例子中,active
和inactive
是我们定义的两种状态。定义状态的样式:
1 | state('inactive', style({ |
这些state
具体定义了每个状态的最终样式,一旦元素转换到了那个状态,该状态的样式就会被应用到此元素上。当它停留在此状态时,这些样式也会一直保留。从这个意义上来讲,这里其实定义的不是动画,而是定义该元素在不同的状态具有的样式。
定义转换
定义完状态,我们只拥有了状态的样式,动画是一种过渡,我们可以定义转换来实现动画的过渡。每个转换都会控制一条在一组样式和下一组样式之间切换的时间线:
1 | transition('inactive => active', animate('100ms ease-in')), |
transition
方法是定义在满足条件时运行的一系列动画步骤:
- 单方向时间线:
preState => nextState
- 两状态相同的时间线:
preState <=> nextState
有时候希望一些样式只在动画期间生效,但是结束后并不保留它们。这时可以把这些样式内联在transition
中定义。例如:
1 | transition('inactive => active', [ |
这样,当转换开始的时候,该元素会立马获得一组样式,然后转换到下一个状态,当转换结束时,状态并不会保留,因为没有定义在state
里面。
*(通配符)状态转换
*(通配符)状态匹配任何动画状态。当定义那些不需要管当前处于什么状态的样式及转换时,很有用:
active => *
当元素的状态从active转换成其他任何状态时,生效* => *
在任意两个状态之间切换时,生效* => active
从任何状态转换到active状态时,生效
void状态转换
有一种特殊的状态void
,它可以应用在任何动画中。它表示元素没有被附加到视图。这种情况可能是由于它未被加进来或者已经被移除了。比如,当一个元素离开视图时,* => void
转换就会生效,而不用管它在离场前是什么状态。
1 | @Component({ |
html:
1 | <button nz-button [nzType]="'primary'" [@heroState]="state" (click)="changeState()">Submit</button> |
上面构造了一个button和div,使用button来控制div的显示和隐藏,同时应用动画上去。
:enter和:leave别名
:enter
和:leave
分别是void => *
和* => void
的别名。这些别名提供多个动画函数使用。定位进入视图的元素比较麻烦,因为它不在DOM中。
例如:
1 | @Component({ |
html:
1 | <div @blockState class="square" *ngIf="show"> |
这样,当元素进入组件的时候,将透明度设为0,然后2s内增长到1。删除这个元素的时候,将透明度设置为0。
:increment和:decrement
待探索
转场中的逻辑值
如果某个触发器以逻辑型的值作为绑定,那么久可以使用能与true
和false
或1
和0
相比较的transition()
表达式来匹配这个值。
1 | @Component({ |
html:
1 | <button nz-button [nzType]="'primary'" [@heroState]="state" (click)="changeState()">Submit</button> |
这样,点击按钮的时候,div会按照show的值进行展示和收缩。
动画回调
当动画启动和终止时,trigger()
函数会发出一些回调。例如,我们对上面的动画接收开始和结束的回调:
1 | <div class="square" |
js:
1 | onAnimationEvent($event) { |
动画回调的潜在用途之一,是用来覆盖比较慢的api调用,比如查询数据,这时可以建立一个循环动画。
关键帧动画
关键帧包括一个用来定义动画中每个样式何时开始更改的偏移属性。偏移是个0到1之间的相对值,分别标记动画的开始和结束时间。定义关键帧的偏移量是可选的,如果省略它们,就会自动分配均匀间隔的偏移。
例如:
1 | <button nz-button [nzType]="'primary'" [@keyframes]="state" (click)="changeState()">Submit</button> |
默认分配均匀间隔的偏移,变化是匀速的:
1 | @Component({ |
自定义的offset偏移量的动画,速率是根据offset来控制:
1 | @Component({ |
带脉动效果的关键帧
通过在整个动画中定义特定偏移处的样式,可以使用关键帧在动画中创建脉动效果。例如:
1 | @Component({ |
angular动画定义在组件的元数据animations
中,使用trigger
函数来定义一组动画,定义的动画的名字可以当做元素的属性来绑定在模板中[@animationTriggerName]
,在trigger
中使用state
函数来定义动画的状态,然后使用transition
函数来定义动画的执行过程,包括状态之间的转换方向。在里面使用animate
来定义动画的执行。