Angular動畫基於W3C的Web Animations標準。不在Angular Core中了。css
組件裏面定義一個或多個觸發器trigger,每一個觸發器有一系列的狀態和過渡效果來實現。html
動畫其實就是從一個狀態過渡到另外一個狀態。狀態自己包含形狀,顏色,大小等。css3
核心是State和Transition。web
State就是定義每一幀狀態npm
Transition是定義一幀到下一幀如何過渡。數組
transition中定義animation,Animate規定了具體怎樣過渡,好比時間,過渡的速度等。Animate有多個重載形式。app
先安裝動畫庫ide
npm i --save @angular/animations
導入modulesvg
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; imports: [ ... BrowserAnimationsModule, //放在最後導入 ],
定義一個寬度100的黑色正方形。函數
.square{ width: 100px; height: 100px; background-color:black; align-items: center; }
在animations元數據中定義一個觸發器,觸發器名字叫square。html的響應元素中[@square]是動畫的觸發器的名字。這個變量或者函數在某一狀況下改變元素的狀態。
<div class="square" [@square]="squareState" (click)="onClick()"> </div>
觸發器有2個重要組成State和Transition。
State定義狀態,在不一樣的狀態下應用不一樣的樣式。經過style把一些css樣式應用於實現動畫的元素,定義好某一幀的顏色,大小,位置。
Transition負責在不一樣狀態切換時候作怎樣的變換,定義如何遷移狀態,如何過渡。
import { trigger, state, transition, style, animate } from '@angular/animations'; @Component({ selector: "app-root", templateUrl: "./app.component.html", styleUrls: ["./app.component.scss"], animations: [ trigger('square', [ state('green', style({ 'background-color': 'green','height':'100px','transform':'translateX(0)' })), state('red', style({ 'background-color': 'red','height':'50px' ,'transform':'translateX(100%)'})), transition('green=>red', animate('.2s 1s')),//動畫持續的時間,延遲多久開始 transition('red=>green', animate(1000)), ]) ] }) export class AppComponent { squareState:string; onClick(){ this.squareState = this.squareState ==='red'?'green':'red'; } }
動畫執行時候的速度,使其看起來更加真實。
由於不是全部的動畫都是勻速的,能夠先加速後減速。
例如:皮球下落,先是越調越快,撞到地上後回彈最終才又碰到地板。
例子:
@Component({ selector: "app-root", templateUrl: "./app.component.html", styleUrls: ["./app.component.scss"], animations: [ trigger('square', [ state('green', style({ 'background-color': 'green','height':'100px','transform':'translateY(-100%)' })), state('red', style({ 'background-color': 'red','height':'100px' ,'transform':'translateY(100%)'})), transition('green=>red', animate('.8s ease-in')), transition('red=>green', animate('.8s ease-out')), ]) ] })
能夠在一個網站看到動畫的速度
能夠參考:
不是全部的cubic-bezier函數都能在css動畫中獲得支持,能夠經過關鍵幀來實現。
keyframes是一個數組,裏面定義每一幀的樣式。
@Component({ selector: "app-root", templateUrl: "./app.component.html", styleUrls: ["./app.component.scss"], animations: [ trigger('square', [ state('green', style({ 'background-color': 'green','height':'100px','transform':'translateY(-100%)' })), state('red', style({ 'background-color': 'red','height':'100px' ,'transform':'translateY(100%)'})), transition('green=>red', animate('.8s ease-in')), transition('red=>green', animate(5000, keyframes([ style({transform:'translateY(100%)'}), style({transform:'translateY(98%)'}), style({transform:'translateY(95%)'}), style({transform:'translateY(90%)'}), style({transform:'translateY(80%)'}), style({transform:'translateY(60%)'}), style({transform:'translateY(30%)'}), style({transform:'translateY(0)'}), style({transform:'translateY(-10%)'}), style({transform:'translateY(-5%)'}), style({transform:'translateY(-2%)'}), style({transform:'translateY(0)'}), style({transform:'translateY(10%)'}), style({transform:'translateY(15%)'}), style({transform:'translateY(-15%)'}), style({transform:'translateY(-40%)'}), style({transform:'translateY(-80%)'}), style({transform:'translateY(-90%)'}), style({transform:'translateY(-95%)'}) ]))) ]) ] })
把動畫分離出來,不要和組件強耦合。
新建一個文件夾animate放全部的動畫文件。
新建一個card.animate.ts放card相關的動畫。
import { trigger, state, transition, style, animate ,keyframes} from '@angular/animations'; export const cardAnim = trigger('card',[ state('out',style({transform:'scale(1)','box-shadow':'none'})), state('hover',style({transform:'scale(1.1)','box-shadow':'3px 3px 5px 6px #ccc'})), transition('out => hover',animate('200ms ease-in')), transition('hover => out',animate('200ms ease-out')) ]);
在project-item裏使用動畫。
一、從@angular/core裏導入HostBinding。
二、引入另一個HostListener
監聽鼠標enter和leave的事件
@HostListener('mouseenter', ['$event.target']) onMouseEnter(target) { this.cardState = 'hover'; } @HostListener('mouseleave', ['$event.target']) onMouseLeave(target) { this.cardState = 'out'; }
import { Component, OnInit, Input, EventEmitter, Output ,HostBinding ,HostListener} from '@angular/core'; import { cardAnim } from '../../animate/card.animate' @Component({ selector: 'app-project-item', templateUrl: './project-item.component.html', styleUrls: ['./project-item.component.scss'], animations:[ cardAnim ] }) export class ProjectItemComponent implements OnInit { @Input() item; @Output() onInvite = new EventEmitter<void>(); @Output() onEdit = new EventEmitter<void>(); @Output() onDelete = new EventEmitter<void>(); @HostBinding('@card') cardState = 'out'; constructor() { } ngOnInit() { } @HostListener('mouseenter') onmouseenter(){ this.cardState = 'hover' } @HostListener('mouseleave') onmouseleave(){ this.cardState = 'out' } onInviteClick() { this.onInvite.emit(); } onEditClick() { this.onEdit.emit(); } onDeleteClick(){ this.onDelete.emit(); } }
不是組件自己的動畫,而是組件中一部分元素的動畫。
新建一個item.animate.ts放task相關的動畫
import { trigger, state, transition, style, animate ,keyframes} from '@angular/animations'; export const itemAnim = trigger('item',[ state('in',style({'border-left-width':'3px'})), state('out',style({'border-left-width':'8px'})), transition('in => out',animate('200ms ease-in')), transition('out => in',animate('200ms ease-out')) ]);
在test-item中使用
import { Component, OnInit, Input, EventEmitter, Output, HostListener } from '@angular/core'; import {itemAnim} from '../../animate/item.animate'; @Component({ selector: 'app-task-item', templateUrl: './task-item.component.html', styleUrls: ['./task-item.component.scss'], animations:[ itemAnim ] }) export class TaskItemComponent implements OnInit { @Input() item; @Input() avatar: string; @Output() taskClick = new EventEmitter<void>(); widerPriority = 'in'; constructor() { } ngOnInit() { this.avatar = this.item.owner ? this.item.owner.avatar : 'unassigned'; } @HostListener('mouseenter') onmouseenter(){ this.widerPriority='out'; } @HostListener('mouseleave') onmouseleave(){ this.widerPriority='in'; } onItemClick() { this.taskClick.emit(); } onCheckBoxClick(event: Event): void { event.stopPropagation(); } }
在模版中用
<mat-list-item class="container" [@item]="widerPriority" [ngClass]="{ 'priority-normal':item.priority===3, 'priority-important':item.priority===2, 'priority-emergency':item.priority===1 }" (click)=onItemClick()> <mat-checkbox [checked]="item.completed" class="status" (click)="onCheckBoxClick($event)"> </mat-checkbox> <div class="content" mat-line [ngClass]="{'completed':item.completed}"> <span [matTooltip]="item.desc">{{item.desc}}</span> </div> <div class="bottom-bar" mat-line> <span class="due-date" *ngIf="item.dueDate"> {{item.dueDate | date:"yy-MM-dd"}} </span> <mat-icon *ngIf="item.reminder"> alarm </mat-icon> </div> <mat-icon [svgIcon]="avatar" mat-list-avatar class="avatar"> </mat-icon> </mat-list-item>
效果