ng 动画

文档

  • 位置、大小、变形、颜色、边框
  • 可以监听动画的开始和结束
  • trigger 定义一个动画名称
  • style({}) 执行的动画
  • animate 执行时间,延迟,过度曲线
  • transition 动画转场
  • State 可以在指定的场景切换
    • => void,void => * 在有和无的状态之间切换

在 state 和 transition 函数中使用样式时有一些需要注意的地方。

请用 state() 来定义那些在每个转场结束时样式,这些样式在动画结束时会保留。

使用 transition() 来定义那些中间样式,以便在动画过程中产生运动的错觉。

当禁用了动画时,也会忽略 transition() 中的样式,但 state() 中的样式不会。

你可以在同一个 transition() 参数中包含多个状态对:
transition( 'on => off, off => void' )

Core模块导入 BrowserAnimationsModule

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@NgModule({
  imports: [ BrowserAnimationsModule],
  exports: [BrowserAnimationsModule]
})
export class CoreModule {}

AppComponent 创建动画

import { Component } from '@angular/core';

import {
  trigger,
  state,
  style,
  animate,
  transition,
} from '@angular/animations';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.styl'],
  animations: [
    // trigger 定义动画名字
    trigger('toggle', [
      
      // 转场时的样式
      state(
        'show',
        style({
          opacity: 1,
        }),
      ),
      state(
        'hide',
        style({
          opacity: 0,
        }),
      ),

      // 转场时的动画效果
      transition('show => hide', [animate('0s')]),
      transition('hide => show', [animate('200ms')]),
    ]),
  ],
})
export class AppComponent {
  public isShow: boolean = true;

  constructor() {}

  public toggle(): void {
    this.isShow = !this.isShow;
  }

  public get animeState() {
    return this.isShow ? 'show' : 'hide';
  }
}

使用:

<div class="container">
  <button (click)="toggle()">toggle</button>
  <div class="box" [@toggle]="animeState"></div>
</div>

配合 *ngIf

import { Component } from '@angular/core';

import {
	trigger,
	state,
	style,
	animate,
	transition,
} from '@angular/animations';

@Component({
	selector: 'app-root',
	templateUrl: './app.component.html',
	styleUrls: ['./app.component.styl'],
	animations: [
		// trigger 定义动画名字
		trigger('toggle', [

			transition(':enter', [ 
				// 动画执行中的动画,结束的时候不会保留
				style({
					opacity: 0,
					'border-radius': '50%',
				}),

				// 动画结束后,保留的样式
				animate('0.2s', style({
					opacity: 1,
					'border-radius': '0%',
					background: 'red'
				})) 
			]),

			transition(':leave', [
				animate('0s', style({
					opacity: 0,
				}))
			]),
		]),
	],
})
export class AppComponent {
	public isShow: boolean = true;

	constructor() {}

	public toggle(): void {
		this.isShow = !this.isShow;
	}
}

使用:

<div class="container">
  <button (click)="toggle()">toggle</button>
	<br>
  <div class="box" @toggle *ngIf="isShow"></div>
</div>

禁用动画

当 @.disabled 绑定为 true 时,就会禁止渲染所有动画

<div class="container" [@.disabled]="true">
  <button (click)="toggle()">toggle</button>
	<br>
  <div class="box" @toggle *ngIf="isShow">
	</div>
</div>

禁用 Angular 应用中的所有动画,只要把 @.disabled 绑定放在顶级的 Angular 组件上即可

export class AppComponent {
  @HostBinding('@.disabled')
  public animationsDisabled = false;
}

监听动画回调

	public toggleAnimationEvent(e:AnimationEvent): void {
		console.log(e.phaseName);
	}

绑定回调事件 @trigger.start 和 @trigger.done

<div class="container">
  <button (click)="toggle()">toggle</button>
	<br>
  <div class="box" 
		@toggle
		(@toggle.start)="toggleAnimationEvent($event)"
		(@toggle.done)="toggleAnimationEvent($event)"
		*ngIf="isShow">
	</div>
</div>

关键帧动画 keyframes

import { Component } from '@angular/core';

import {
	trigger,
	state,
	style,
	animate,
	transition,
	keyframes,
	AnimationEvent
} from '@angular/animations';

@Component({
	selector: 'app-root',
	templateUrl: './app.component.html',
	styleUrls: ['./app.component.styl'],
	animations: [
		// trigger 定义动画名字
		trigger('toggle', [

			transition(':enter', [ 
				animate('2s',
					keyframes([
						style({ background: 'blue', offset: 0 }),
						style({ background: 'green', 'margin-left': 200, offset: 0.3 }),
						style({ background: 'yellow', 'margin-left': 0, offset: 0.5 }),
					]),
					style({
						opacity: 1,
						'border-radius': '0%',
						background: 'red'
					})) 
			]),

			transition(':leave', [
				animate('0s', style({
					opacity: 0,
				}))
			]),
		]),
	],
})
export class AppComponent {
	public isShow: boolean = true;

	constructor() {}

	public toggle(): void {
		this.isShow = !this.isShow;
	}
}

使用:

<div class="container">
  <button (click)="toggle()">toggle</button>
	<br>
  <div class="box" 
		@toggle
		*ngIf="isShow">
	</div>
</div>

使用通配符自动计算属性 文档

animations: [
  trigger('shrinkOut', [
    state('in', style({ height: '*' })),
    transition('* => void', [
      style({ height: '*' }),
      animate(250, style({ height: 0 }))
    ])
  ])
]

动画序列 文档

列表的 item 连续进场

import { Component, HostBinding } from '@angular/core';

import {
	trigger,
	state,
	style,
	animate,
	transition,
	keyframes,
	AnimationEvent,
	trigger,
	query,
	stagger
} from '@angular/animations';

@Component({
	selector: 'app-root',
	templateUrl: './app.component.html',
	styleUrls: ['./app.component.styl'],
	animations: [
		trigger('pageAnimations', [
			transition(':enter', [
				query('ul li', [
					style({opacity: 0, transform: 'translateX(-100px)'}),
					stagger(200, [
						animate('500ms cubic-bezier(0.35, 0, 0.25, 1)', style({ opacity: 1, transform: 'none' }))
					])
				])
			])
		]),
	],
})
export class AppComponent {
	@HostBinding('@pageAnimations')
	public isShow: boolean = true;
	public names: string[] = ["ajanuw", "alone", "suou"];

	constructor() {}

	public toggle(): void {
		this.isShow = !this.isShow;
	}
}

使用:

<div class="container">
  <button (click)="toggle()">toggle</button>
	<br>
	<ul *ngIf="isShow" @pageAnimations>
		<li *ngFor="let name of names">{{ name }}</li>
	</ul>
</div>
ul {
	list-style-type: none;
	padding: 0;
	li {
		padding 8px;
		margin: 8px;
                background-color: #ff00003b;
	}
}

group() 动画属性设置不同的速度曲线

animations: [
    trigger("pageAnimations", [
      transition(":enter", [
        query("ul li", [
          style({ opacity: 0, transform: "translateX(-100px) rotate(2deg)" }),
          stagger(200, [

            group([
              animate(
                "500ms cubic-bezier(0.35, 0, 0.25, 1)",
                style({ transform: "translateX(0) rotate(0deg)" })
              ),
              animate(
                "700ms cubic-bezier(0.28, 0.08, 0.78, 1.13)",
                style({ opacity: 1, })
              ),
            ]),

          ]),
        ]),
      ]),
    ]),
  ],

sequence 依次执行动画属性

  animations: [
    trigger("pageAnimations", [
      transition(":enter", [
        query("ul li", [
          style({ opacity: 0, transform: "translateX(-100px)" }),
          stagger(200, [
            sequence([
              animate(
                "200ms cubic-bezier(0.35, 0, 0.25, 1)",
                style({ opacity: 1 })
              ),
              animate(
                "2s cubic-bezier(0.35, 0, 0.25, 1)",
                style({ transform: "translateX(0)" })
              ),
            ]),
          ]),
        ]),
      ]),
    ]),
  ],

可复用动画 文档

路由转场动画 文档

import { Component } from "@angular/core";
import { RouterOutlet } from "@angular/router";

import {
  trigger,
  transition,
  style,
  query,
  animateChild,
  group,
  animate,
} from "@angular/animations";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.styl"],
  animations: [
    trigger("routeAnimations", [
      // * 转场状态必须和路由配置中定义的 data 属性的值相一致
      transition("HomePage <=> AboutPage", [
        // 转场期间,新视图将直接插入在旧视图后面
        // 并且这两个元素会同时出现在屏幕上。要防止这种情况,就要为宿主视图以及要删除和插入的子视图指定一些额外的样式
        // 宿主视图必须使用相对定位模式,而子视图则必须使用绝对定位模式
        // 这些视图中添加样式,就可以让容器就地播放动画,而不会让 DOM 移动
        style({ position: "relative" }), // 宿主视图
        query(":enter, :leave", [
          // 子视图
          style({
            position: "absolute",
            top: 0,
            left: 0,
            width: "100%",
          }),
        ]),

        // query(":enter") 语句会返回已插入的视图
        query(":enter", [
          style({
            transform: "translateX(-100%)",
          }),
        ]),

        // query(":leave") 语句会返回已移除的视图
        query(":leave", animateChild()), // 调用 animateChild(),来运行其子动画

        group([
          query(":leave", [
            animate(
              "300ms ease-out",
              style({
                transform: "translateX(100%)",
              })
            ),
          ]),
          query(":enter", [
            animate(
              "300ms ease-out",
              style({
                transform: "none",
              })
            ),
          ]),
        ]),

        query(":enter", animateChild()),
      ]),
    ]),
  ],
})
export class AppComponent {
  constructor() {}

  prepareRoute(outlet: RouterOutlet) {
    return (
      outlet &&
      outlet.activatedRouteData &&
      outlet.activatedRouteData["animation"]
    );
  }
}

使用:

<div>
  <div>
    <a [routerLink]="['/header']">header</a>
    <a [routerLink]="['/footer']">footer</a>
  </div>
  <div [@routeAnimations]="prepareRoute(outlet)">
    <router-outlet #outlet="outlet"></router-outlet>
  </div>
</div>

路由定义:

const routes: Routes = [
  {
    path: "header",
    component: HeaderComponent,
    data: { animation: "HomePage" },
  },
  {
    path: "footer",
    component: FooterComponent,
    data: { animation: "AboutPage" },
  },
];
posted @ 2019-01-29 11:02  Ajanuw  阅读(63)  评论(0)    收藏  举报