动画转场:通配符与void
预定义状态与通配符匹配
在 Angular 中,转场状态可以通过 state ()函数进行显式定义,或使用预定义的*(通配符)状态和void状态。
在Angular中,转场的动画,可以通过state() 函数进行显示定义,也可以通过Angular预定的 *(通配符) 状态 和void状态。
通配符
“*” 通配符:可以匹配任何一个动画的状态。当不在乎某个HTML元素的起始状态或者结束状态的时候,就可以用到通配符。
例如: 在Angular学习笔记32中,
<div [@stateChange]="isChange?'isYellow':'isGreen'">
<p>
Angular学习笔记
</p>
</div>
当不在乎
使用多个转场状态的通配符(*)
有时候在做动画的时候,会遇到,对于某一个元素,会有不止两个的状态,例如如下:
animations: [
// 定义动画的触发器放在 animations 元数据属性中*
trigger('stateChange', [
state('isYellow', style(
{
height: '200px',
width: '200px',
opacity: 1,
backgroundColor: 'yellow'
}
)),
state('isGreen', style(
{
height: '100px',
width: '100px',
opacity: 0.5,
backgroundColor: 'green'
}
)),
state('isRed', style(
{
height: '100px',
width: '100px',
opacity: 0.5,
backgroundColor: 'red'
}
)),
transition('isYellow => isGreen', animate('1s')),
transition('isGreen => isYellow', animate('2s')),
])
],
假设需要 isYellow 的状态和 isGreen 的状态相互转换的时候,都需要1s时间,正常情况如下写:
transition('isYellow => isGreen', animate('1s')),
transition('isGreen => isYellow', animate('1s')),
此时就可以使用双向箭头语法(<=>),从而指定任意方向的转场,故上面的代码可以简化为:
transition('isYellow <=> isGreen', animate('1s')),
在这里定义了有三个状态分别是:isYellow,isGreen,isRed,这三种状态。
这个时候,在状态于状态之间转换的时间是设定这里,在这个元素向isRed这个状态的时候,都需要0.5s转场时间,这个时候,就不用去写如下:
transition('isYellow => isRed', animate('0.5s')),
transition('isGreen => isRed', animate('0.5s')),
这个时候就可以使用通配符了,故上面的代码就可以简化为:
transition('* => isRed', animate('0.5s')),
在这个时候,我们在设置这三种状态在任意状态转化的时候,都要经历5s,从而验证一下动画匹配的优先级。
transition('* => *', animate('5s')),
从而animations数组具体如下:
animations: [
// 定义动画的触发器放在 animations 元数据属性中*
trigger('stateChange', [
state('isYellow', style(
{
height: '200px',
width: '200px',
opacity: 1,
backgroundColor: 'yellow'
}
)),
state('isGreen', style(
{
height: '100px',
width: '100px',
opacity: 0.5,
backgroundColor: 'green'
}
)),
state('isRed', style(
{
height: '150px',
width: '150px',
opacity: 0.5,
backgroundColor: 'red'
}
)),
transition('isYellow <=> isGreen', animate('1s')),
transition('* => isRed', animate('0.5s')),
transition('* => *', animate('5s')),
])
],
修改模版文件为如下:
<div [@stateChange]="isChange">
<p>
Angular学习笔记
</p>
</div>
<button nz-button (click)="handleChange('isRed')">改变为红色</button>
<button nz-button (click)="handleChange('isYellow')">改变为黄色</button>
<button nz-button (click)="handleChange('isGreen')">改变为绿色</button>
修改类文件如下:
import {Component, OnInit} from '@angular/core';
import {trigger, state, style, animate, transition} from '@angular/animations';
@Component({
selector: 'app-animation-demo',
templateUrl: './animation-demo.component.html',
styleUrls: ['./animation-demo.component.less'],
animations: [
// 定义动画的触发器放在 animations 元数据属性中*
trigger('stateChange', [
state('isYellow', style(
{
height: '200px',
width: '200px',
opacity: 1,
backgroundColor: 'yellow'
}
)),
state('isGreen', style(
{
height: '100px',
width: '100px',
opacity: 0.5,
backgroundColor: 'green'
}
)),
state('isRed', style(
{
height: '150px',
width: '150px',
opacity: 0.5,
backgroundColor: 'red'
}
)),
transition('isYellow <=> isGreen', animate('1s')),
transition('* => isRed', animate('0.5s')),
transition('* => *', animate('5s')),
])
],
})
export class AnimationDemoComponent implements OnInit{
public isChange = false;
constructor() {
}
ngOnInit() {
}
public handleChange(state): void {
this.isChange = state;
}
}
通过上面的动画可以看到,当任意状态向红色变更的时候,转场:
transition('* => *', animate('5s')),
被覆盖,使用了转场:
transition('* => isRed', animate('0.5s')),
从而可以看到,* => *优先级最低,而后才是 * => isRed;
将三个转场调顺如下:
animations: [
// 定义动画的触发器放在 animations 元数据属性中*
trigger('stateChange', [
state('isYellow', style(
{
height: '200px',
width: '200px',
opacity: 1,
backgroundColor: 'yellow'
}
)),
state('isGreen', style(
{
height: '100px',
width: '100px',
opacity: 0.5,
backgroundColor: 'green'
}
)),
state('isRed', style(
{
height: '150px',
width: '150px',
opacity: 0.5,
backgroundColor: 'red'
}
)),
transition('* => *', animate('5s')),
transition('isYellow <=> isGreen', animate('1s')),
transition('* => isRed', animate('0.5s')),
])
],
然后运行保存,动画如下:
从而可以验证,如果要确保特殊的转场要被使用,就要保证,所有特殊的转场动作都要放在transition(’* => *’, animate(‘5s’)), 之前。
- 在样式中使用通配符(*)
transition(‘* => isRed’, animate(‘0.5s’, style({opacity: ‘*’}))),
在这个时候,动画就会使用当前的状态的配置值,从而进行动画。
特殊状态:void状态
具体:Void状态来为进入或离开页面的元素配置转场,也可以理解为:将元素从DOM中插入或者删除。
组合使用通配符(*)和void状态你可以在转场中组合使用通配符和void状态,以触发那些进入和离开页面的动画:
-
当元素离开视图时(这个元素从DOM删除的时候),就会触发* => void转场,而不管它离开前处于什么状态。
-
当元素进入视图时(这个元素被插入到DOM的时候),就会触发void => *转场,而不管它进入时处于什么状态。
注意:通配符状态(*)会匹配任何状态(包括void)。
现在来做一个将一个元素插入DOM的时候从左飞入,删除DOM的时候,从右边飞出的动画
修改模版文件为如下:
<div [@stateChange]="isChange">
<p>
Angular学习笔记
</p>
</div>
<button nz-button (click)="handleChange('isRed')">改变为红色</button>
<button nz-button (click)="handleChange('isYellow')">改变为黄色</button>
<button nz-button (click)="handleChange('isGreen')">改变为绿色</button>
<nz-divider [nzText]="'飞入飞出动画'"></nz-divider>
<nz-row>
<nz-form-control [nzSpan]="4" nzOffset=*"10">
<div>
飞入飞出动画~
</div>
</nz-form-control>
</nz-row>
添加元素插入DOM 的状态:
state('flyIn', style({
transform: 'translateX(0)',
color: 'blue',
})),
添加进入视图的转场(插入DOM):
transition('void => *', [
style({
transform: 'translateX(-100%)',
color: 'yellow'
}),
animate('2s')
]),
添加离开视图的转场(从DOM中删除):
transition('* => void', [
animate('2s', style({
transform: 'translateX(100%)',
color: 'red'
})),
])
将上述添加到一个触发器中:
trigger('fly', [
state('flyIn', style({
transform: 'translateX(0)',
color: 'blue',
})),
transition('void => *', [
style({
transform: 'translateX(-100%)',
color: 'yellow'
}),
animate('2s')
]),
transition('* => void', [
animate('2s', style({
transform: 'translateX(100%)',
color: 'red'
})),
])
])
在类文件中增加一个属性来控制元素的插入和删除
export class AnimationDemoComponent implements OnInit {
public isChange = false;
public isInOut = false;
constructor() {
}
ngOnInit() {
}
public handleChange(state): void{
this.isChange = state;
}
public handleInOut() {
this.isInOut = !this.isInOut;
}
}
在Angular中,可以使用*ngIf指令来控制将元素从DOM删除或者插入。故修改模版文件:
<div [@stateChange]="isChange">
<p>
Angular学习笔记
</p>
</div>
<button nz-button (click)="handleChange('isRed')">改变为红色</button>
<button nz-button (click)="handleChange('isYellow')">改变为黄色</button>
<button nz-button (click)="handleChange('isGreen')">改变为绿色</button>
<nz-divider [nzText]="'飞入飞出动画'"></nz-divider>
<nz-row>
<button nz-button (click)="handleInOut()">按钮</button>
</nz-row>
<nz-row>
<nz-form-control nzSpan="10" nzOffset="7">
<div style="text-align: center;background: red" *ngIf="isInOut" [@fly]="isInOut?'flyIn':'void'">
飞入飞出动画~
</div>
</nz-form-control>
</nz-row>
保存运行后,可以看到:
两个重要的状态别名(:enter 和 :leave)
在Angular中:enter 和 :leave 分别是 void => * 和 * => void 的别名。
所以之前的代码就也可以写成:
transition(':enter', [
style({
transform: 'translateX(-100%)',
color: 'yellow'
}),
animate('2s')
]),
transition(':leave', [
animate('2s', style({
transform: 'translateX(100%)',
color: 'red'
})),
])
这样也可以达到同样的效果。