Angular4之动画

在angular里面,动画的本质,是在一定时间,由一个状态转换到另一个状态,期间的过渡效果就是显示出来就是动画。
例如:

import {
    Component,Input
} from '@angular/core';
import {
    trigger,  state,  style, animate, transition
} from '@angular/animations';
import { Hero } from './hero.service';
@Component({
    selector: 'app-hero-list-basic',
    template: `
        <ul>
            <li *ngFor="let hero of heroes"   [@heroState]="hero.state"  (click)="hero.toggleState()"> {{hero.name}} </li>
        </ul>`,
    styleUrls: ['./hero-list.component.css'],
    animations: [
        trigger('heroState', [
            state('inactive', style({
                backgroundColor: '#eee',
                transform: 'scale(1)'
            })),
            state('active', style({
                backgroundColor: '#cfd8dc',
                transform: 'scale(1.1)'
            })),
            transition('inactive => active', animate('100ms ease-in')),
            transition('active => inactive', animate('100ms ease-out'))
        ])
    ]
})

动画状态是由一个程序代码中定义的字符串值,在上面的例子中,基于英雄对象的逻辑状态,我们使用了‘active’和‘inactive’这两种状态。
状态的来源是可以向本例子中这样简单的对象属性,也可以是由方法计算出来的值,重点是,我们得从组建模板中读取它。
我们为每个动画的状态定义了一组样式:

*//样式写在state里面
state('inactive', style({
    border: '1px solid black',
    color: 'black',
    transform: 'scale(1)'
})),
state('active', style({
    border: '1px solid white',
    color: 'white',
    transform: 'scale(1.5)'
})),
transition('inactive => active', animate('5000ms ease-in')),
transition('active => inactive', animate('100ms ease-out'))

动画结果:
inactive->active:在5s内,黑色字体、黑色边框渐变成白色字体、白色边框;
active->inactive:在0.1s之内,由白色边框、白色字体,变成黑色边框黑色字体;

这些state具体定义了每个状态的最终样式,一旦转场到那个状态。该样式就会被应用到此元素上,当它停留在此状态时,这些样式也会一直保持着,
从这个意义上讲,这里其实并不只是在定义动画,而是在定义该元素在不同状态时应该具有的样式。
定义完状态,就能定义在状态之间的各种转场了,每个转场都会控制一条在一组样式之间切换的时间线。

transition('inactive => active', animate('100ms ease-in')),
transition('active => inactive', animate('100ms ease-out'))

当多个转场的时间线相同时,还可以这样简写

transition('inactive => active,active => inactive'', animate('100ms ease-in'));

如果对同一个转场的两个方向都使用相同的时间线,还可以这样简写

transition('inactive<=> active'', animate('100ms ease-in'));

样式写入的位置不同,会有不同的效果
*//样式写在transition里面
有时候,希望一些样式只在动画期间生效,但在结束后并不保留它们,这时候,可以把这些样式内联在transition中进行定义,在这个例子中,该元素会立刻获得一组样式(字体颜色为红色),然后动态转场到下一个状态,当转场结束时,这些样式并不会保留,因为它们并没有被定义在state中。

state('inactive', style({
    border: '1px solid black',
    color: 'black',
    transform: 'scale(1)'
})),
state('active', style({
    border: '1px solid white',
    color: 'white',
    transform: 'scale(1.5)'
})),
transition('inactive => active', [
    style({ color: 'red' }),
    animate('5000ms ease-in')]
),
transition('active => inactive', animate('100ms ease-out'))

动画结果:
inactive->active:字体颜色立即变为红色,然后在5s内,红色字体、黑色边框渐变成白色字体、白色边框,并且放大1.5倍;
active->inactive:由白色边框、白色字体,在0.1s之内,变成黑色边框、黑色字体缩小1.5倍,不会再出现红色;
即宿主元素会立即获得红色字体的样式,然后再从该样式切换active状态,但是该样式不会保留,所以,从active切换到inactive时,并不会出现这一样式。

*//样式写在animate里面
state('inactive', style({
    border: '1px solid black',
    color: 'black',
    transform: 'scale(1)'
})),
state('active', style({
    border: '1px solid white',
    color: 'white',
    transform: 'scale(1.5)'
})),
transition('inactive => active', [
    style({ color: 'red' }),
    animate('5000ms ease-in',
        style({
            color: 'blue'
        })
    )]
),
transition('active => inactive', animate('100ms ease-out'))

动画结果:
inactive->active:字体立即变成红色,然后5s中,由红色变成蓝色,最后边框和字体都变成白色;
active->inactive:由白色边框、白色字体,在0.1s之内,变成黑色边框黑色字体,不会再出现红色、蓝色;
animate中的style会参与动画,但是不会被保留。

*状态

*通配符状态匹配任何动画状态,当定义那些不需要管当前处于什么状态的样式及转场时,很有用。
使用场景:
当该元素的状态从active变成任何其他状态时,active=>*转场都会生效
当在任意两个状态之间切换时,=>转场都会生效

void状态

有一种叫做void的特殊状态,它可以应用在任何动画中,它表示元素没有被附加到视图,这种情况可能是由于它尚未被添加进来,或者已经被移除了。
void状态在定义进场、离场的动画时会非常有用比如,当一个元素离开视图时,=>void转场就会生效,而不管它在离场以前是什么状态,也能匹配void。可以直接将元素的状态定义为void。
例子:

state('inactive', style({ transform: 'translateX(0)' })),
    transition('void => *', [
        style({ transform: 'translateX(-100%)' }),
        animate(1000)
    ]),
transition('* => void', [
animate(1000, style({ transform: 'translateX(100%)' }))
]),
transition('inactive => active', animate('5000ms ease-in')),
transition('active => inactive', animate('1000ms ease-out'))

动画结果:
在初始化的时候:先立即左移到100%的位置,然后,当前是在动画开始阶段即void阶段,在1000ms内,右移到state的位置

*自动属性值计算
有时候,我们想在动画中使用的尺寸类样式,它的值在开始运行之前都是不可知的。
比如,元素的宽度和高度往往依赖于它们的内容和屏幕的尺寸。处理这些属性对CSS动画而言通常是相当棘手的。
如果用Angular动画,就可以用一个特殊的*属性值来处理这种情况。该属性的值将会在运行期被计算出来,然后插入到这个动画中。

state('inactive', style({ height: '0'  })),
state('active', style({ backgroundColor: 'red', height: '*' })),
transition('inactive => active', animate('1000ms ease-in')),
transition('active => inactive', animate('1000ms ease-out'))

动画结果:
inactive->active:在1s之内,高度由0变为原始高度,背景色为红色;
active->inactive:在1s之内,由原始高度,背景色为红色,高度变为0,背景色渐渐消失;

*基于关键帧(keyframes)的多阶段动画

通过定义动画的关键帧,可以把两组样式之间的简单转场,升级成一种更复杂的动画,它会在转场期间经历一个或多个样式;
每个关键帧都可以指定一个偏移量,用来定义该关键帧将被用在动画期间的哪个时间点,

注:offset这个偏移量并不是用绝对数字定义的时间段,而是在0到1之间的相对值(百分比)。动画的最终时间线会基于关键帧的偏移量、持续时间、延迟和缓动函数计算出来。为关键帧定义偏移量是可选的。如果省略它们,偏移量会自动根据帧数平均分布出来。
例如,三个未定义过偏移量的关键帧会分别获得偏移量:0、0.5和1即时间线的比例。

state('inactive', style({ transform: 'translateX(0)' })),
transition('void => *', [
    animate(3000, keyframes([
        style({ opacity: 0, transform: 'translateX(-100%)', offset: 0 }),
        style({ opacity: 1, transform: 'translateX(15px)', offset: 0.3 }),
        style({ opacity: 1, transform: 'translateX(0)', offset: 1.0 })
    ]))
]),
transition('* => void', [
    animate(3000, keyframes([
        style({ opacity: 1, transform: 'translateX(0)', offset: 0 }),
        style({ opacity: 1, transform: 'translateX(-15px)', offset: 0.7 }),
        style({ opacity: 0, transform: 'translateX(100%)', offset: 1.0 })
    ]))
])

注:偏移量要按从小到大的顺序排列,否则会报错。

动画结果:
进场:样式初始化为opacity:0,translateX(-100%),然后,在(3000*0.3s)内变为opacity:1,translateX(0),接下来在(3000*0.7s)内,由当前状态,左移15px,再1s内又右移15px。
离场:则完全相反。

*并行动画组

group代表里面有很多个动画,动画没有延迟,则同时执行,若有延迟,则按延迟的时间大小执行,延迟时间越小,越先执行;

animate(timing,style?);
state('inactive', style({ transform: 'translateX(0)', opacity: 1 })),
    transition('void => *', [
        style({ transform: 'translateX(50px)', opacity: 0 }),
    group([
        animate('1.3s 2s ease',
            style({  transform: 'translateX(0)' })
        ),
        animate('2.3s ease', style({ opacity: 1 }))
     ])
])

动画效果:初始阶段,宿主元素在原地,动画一触发,则立即往右平移50px;
而后,在2.3s内,opacity由0变为1,并且在动画组里的的动画开始2s之后左移到inactive状态的位置;

*动画时间线

timing:  AnimateTimings;
export declare type AnimateTimings = {
    duration: number;
    delay?: number;
    easing?:string | null;
};

duration:持续事件控制动画从开始到结束要花多久的时间,方式: 100 100ms 0.1s 时间均相等;
delay:延迟控制的是在动画已经出发但尚未真正开始转场之前要等待多久,可以把它添加到字符串中的持续事件后面 eg: ‘100ms 1s’ 代表延迟1s,然后动画运行100ms;
easing:缓动函数用于控制动画在运行期间如何加速减速,eg:ease-in函数意味着动画开始相对缓慢,然后在进行中逐步加速。例如:ease、 linear 、ease-in 、 cubic-bezier(0.4,0,0.2,1)、 ease-in-out等等;
注:参数可以省略,但是顺序是固定的。

动画回调:

当动画开始和结束时,会触发一个回调
这些回调接收一个AniamtionTransition参数,它包含一些有用的属性,例如fromState,toState和totalTime。
无论动画是否实际执行过,那些回调都会触发。

html:
<p class="p" [@rotateIn] (@rotateIn.start)='animationStarted($event)' (@rotateIn.done)='animationDone($event)'>rotateIn</p>

ts:
animationStarted(evt: any) {
    console.log('animation start', evt)
}
animationDone(evt: any) {
    console.log('animation done', evt)
}

*useAnimation
例子:

//define
export const fadeAnimation = animation([
    style({ opacity: '{{ start }}' }),
    animate('{{ time }}',
        style({ opacity: '{{ end }}' })
    )], {    params: { time: '1000ms', start: 0, end: 1 }
});
//register
 animations: [trigger('fadeAnimation', [transition('*=>*', useAnimation(fadeAnimation, {
        params: {
             time: '5s',
             start: 1,
             end: 0
        }
 }))])]

use:

一、<div class="test" [@fadeAnimation]>
    <p>11111</p>
</div>
二、<div class="test" [@fadeAnimation]="{ value:testState,params: {time: '5s',start: 1,end: 0}}">
    <p>11111</p>
</div>

注:当像二那么使用的时候,不会报错,但是传进去的参数并没有起作用,因为在触发的时候已经传参进去了,动画的实际采纳数就是trigger时的参数了。
or

//register
 animations: [trigger('fadeAnimation', [transition('*=>*', useAnimation(fadeAnimation))])]
//use
<div class="test" [@fadeAnimation]="{ value:testState,params: {time: '5s',start: 1,end: 0}}">
    <p>11111</p>
</div>

参数中的value值就是该动画函数的状态,所以更改状态即可触发动画函数。

query

用于查找当前元素内的一个或多个内部元素,这些元素在序列中被激活
所提供的动画步骤应用于所查询的元素(默认情况下,提供一个动画数组,将其作为一个动画序列来处理)
例如:

transition('* => *', [
    // hide the inner elements
    query('h1', style({ opacity: 0 }), { optional: true }, ),
    query('.querybox', style({ opacity: 0 }), { optional: true }, ),
    // animate the inner elements in, one by one
    query('h1', [animate(1000, style({ opacity: 1 }) ], { optional: true }),
    query('.querybox ', [animate(3000, style({ opacity: 1 }) ], { optional: true })
])
html:
<div [@testAnimation]='testState'>
    <h1>title</h1>
    <div class="querybox">
        bbbb contentg
        <h1>inner h12</h1>
    </div>
</div>

动画效果:在1s内,中放入title,由opacity:0转变为opacity:1,经过1s后,在3s内,querybox由opacity:0转变为opacity:1

limit:

用来被限制个数

query('h1', style({ opacity: 0 }), { limit: 1, optional: true }),//在这代表只限制一个应用该样式
query('.querybox', style({ opacity: 0 }), { optional: true }, ),
// animate the inner elements in, one by one
query('.querybox ', [animate(3000, style({ opacity: 1, color: 'blue' }) ], { optional: true }),
query('h1', [animate(5000, style({ opacity: 1, color: 'red' }) ], { limit: 1, optional: true }),

//在这代表只限制一个应用该动画
动画效果:在3s内,querybox由opacity:0转变为opacity:1,经过3s后,在1s内,h1的title由opacity:0转变为opacity:1
querybox中的h1不在之内,因为限制了一个,所以只应用于查找到的第一个,类似于firstchild。

keyframes 与group的区别

不同点:
keyframs是按offset的顺序来,操作的css属性可以相同,也可以不同;
group中的animate是同时触发;
当group里面的animate中,多个animate对同一个css属性做动画时,排在前面的动画时间不能与后面的动画有冲突;
也就是说,前一个动画必须执行完了,才能执行第二个动画,同时,前面的动画,并没有动画效果,与写在transition里面无异。
适用场景:
keyframs适用于对同一个属性做不同的变化
group适用于对不同的css属性,做动画,同时两个属性的缓动函数不相同。

未完待续…

猜你喜欢

转载自blog.csdn.net/zhy13087344578/article/details/79110412