原文:Angular — Supercharge your Router transitions using new animation features (v4.3+)javascript
首先咱们看一下效果展现的demohtml
Basicjava
Variationgit
Staggergithub
Finalweb
为了介绍这个新的动画,咱们将用一个只有home和about页面的简单应用来作演示。咱们将要用内容向左飞出而后用下图所示的交错进入的效果实现一个很酷的路由页面切换bootstrap
独立引入Angular 4里的动画部分,这样可使你的项目更轻便,若是你不想用动画的话只要直接拿掉就能够了后端
首先,咱们把下列依赖独立引入你的项目:@angular/animations
和 @angular/platform-browser/animations
接下来在项目中的入口模块的代码引入 BrowserAnimationsModule
数组
\\app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @NgModule({ imports: [ BrowserModule, BrowserAnimationsModule, ... ], declarations: [ App, ... ], bootstrap: [ App ] }) export class AppModule { }
若是你须要使用 不支持原生WEB动画API的浏览器 的话:好比IE、Edge、Safari等等。你必需要引入web-animations依赖浏览器
正如咱们所见,咱们的例子将由顶部导航和主体内容部分组成。各个内容部分将共享同一个导航栏,咱们使用router-outlet
元素来通知路由咱们须要渲染组件的地方和须要匹配路由的地方
\\app.module.ts @Component({ selector: 'my-app', template: ` <nav> <a routerLink="/home" routerLinkActive="active">Home</a> <a routerLink="/about" routerLinkActive="active">About</a> </nav> <main> <router-outlet></router-outlet> </main> ` }) export class App { }
咱们使用静态路由来建立不一样的导航连接。而后咱们用routerLinkActive
指令来装饰当前的内容部分。举个例子,若是咱们要导航到首页Home,导航连接的标签上将加上active
选择器并根据这个渲染对应部分
<a href="#/home" class="active">Home</a>
让咱们改变默认的路由变换设置。首先咱们须要加上动画触发器属性 routerTransition
,而后咱们绑定@routerTransition
在主标签元素上,以后,咱们能够装饰内部的路由组件并交由Angular的路由来实例化
咱们能够用一个div或者其余标签来替换主标签元素,只要它是
router-outlet
的父结点
咱们用getState
方法传递外部设置的参数来设置正确的状态。这个方法将返回一个 state
的用于设置路由定义的属性。咱们接下来看下一个部分,这个设置将容许咱们去控制每一个路由将执行哪一种切换
提示:咱们能够经过使用
#o="outlet"
来得到外部传参,因为在router-outlet definition里使用exportAs使之成为可能。这给了咱们一个快速获取底层RouterOutlet选择器类的途径
\\app.module.ts @Component({ selector: 'my-app', animations: [ routerTransition ], template: ` <main [@routerTransition]="getState(o)"> <router-outlet #o="outlet"></router-outlet> </main> ` }) export class App { getState(outlet) { return outlet.activatedRouteData.state; } }
在Angular里面,路由会去尝试匹配路由定义和当前的url,同一个生效的配置优先级由上到下
\\app.routing.ts const routes = [ { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: 'home', component: Home, data: { state: 'home' } }, { path: 'about', component: About, data: { state: 'about' } }, { path: '**', component: NotFound } ]; export const AppRouting = RouterModule.forRoot(routes, { useHash: true });
路由设置告诉路由当匹配到Home和About页面的路径时去实例化对应的组件。注意这个data
属性设置每一个状态给对应的路由。这些状态将要去匹配咱们在routerTransition
中设置的切换
咱们也须要设置两个为了一般展现的特殊路由。这个空路径的路由将提供一个默重定向到主页的路由以防导航去了一个非咱们须要的路径。其余的路由或者未定义的路由展现一个用户友好的404页面就好
为了这个例子,由于Plunker的缘由咱们使用一个哈希定位的策略。若是咱们须要后端咱们能够沿用普通的路径跳转,可是若是没有后端来展现404错误的话咱们就使用这个策略来重定向未知路径到index.html
让咱们隆重介绍Angular动画。Angular是基于最新的Web Animations API,咱们使用动画触发器(animation triggers)来定义一系列状态和变换属性。咱们也能够用CSS样式来改写实现咱们想要的效果
主要的原则是开始和结尾的动画样式由咱们自定义,中间变换的计算过程交给工具自己
固然,能够经过设置时间来设置中间动画,好比1s,1.2s,200ms。其余的就是你们熟悉的CSS动画的速度属性好比ease
、liner
和ease-in-out
。
而Angular 4.2以上的版本里咱们能够用顺序(sequence)和组合(group)来让动画一个接一个执行仍是同时执行;查询(query)能够操做子元素而交错(stagger)能够创造一个很棒的连锁效果
这些事件将触发一个动画:
[@routerTransition]="home"
在路由转换的先后关系中,要注意,组件正在被移除并做为导航的一部分被添加到视图中的过程。
首先让咱们看一下如何使用Angular动画来让内容向左滑出
最初,定义咱们的触发器routerTransition
。在这个实现中,咱们使用了一个覆盖全部可能状态的通用转换
这是个经过两个独立转换* => home
、 * => about
完成的重写
咱们能够在咱们的转换中用两个特殊标识
void
和*
来表明:一个元素还没有被挂载到视图或者任何状态
\\router.animations.ts import {trigger, animate, style, group, animateChild, query, stagger, transition} from '@angular/animations'; export const routerTransition = trigger('routerTransition', [ transition('* <=> *', [ /* order */ /* 1 */ query(':enter, :leave', style({ position: 'fixed', width:'100%' }) , { optional: true }), /* 2 */ group([ // block executes in parallel query(':enter', [ style({ transform: 'translateX(100%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(0%)' })) ], { optional: true }), query(':leave', [ style({ transform: 'translateX(0%)' }), animate('0.5s ease-in-out', style({ transform: 'translateX(-100%)' } ], { optional: true }), ]) ]) ])
咱们在第二个参数里定义了一个顺序执行通用转换的数组 。第一个参数呢,使用了一个新的查询指令去选择新的路径组件和离开的组件;查询指令可使用相似CSS伪类的选择器方式选中,好比一些特殊的关键词::enter
,:leave
和*
在第一次查询中,:enter
和:leave
将会匹配被挂载和卸载的组件,多个选择器用逗号分开。一旦咱们有了这些路由组件,咱们能够设置他们的样式去达到滑动的效果。使用 { position: fixed }
组件将根据视口放置,并滑过页面。
接下来,咱们将设置一个组合使得内部动画同时执行。
让咱们想象一下,咱们从Home切换到About页面,第一次查询将会匹配这个被加了:enter
的组件,那就是About组件。咱们首先在右边很远的地方设置动画,而后让它缓缓的进入滑动到固定的位置。结果就是About组件从右边滑到左边。第二次查询中,咱们用了一个类似的路径,使用:leave
来匹配一样在左边很远处的Home组件
CSS小提示:为了更好的表现,咱们使用transform取代top,bottom,left,right
注意这种实现方式与咱们将触发器绑定到咱们想要动画的元素的动画中的用例不一样。
这意味着咱们不能使用状态来对路由组件进行样式,由于这样能够将样式应用于父结点(咱们的示例中的主元素),而不是路由组件。
在路由初始化期间,一些查询指令会返回空的结果,为了不抛出错误,咱们在全部查询中设置了可选参数。
一旦咱们的基础部分完成了,咱们能够很容易在这个基础上用查询和交错控制加上一些新的效果
export const routerTransition = trigger('routerTransition', [ transition('* <=> *', [ /* order */ /* 1 */ query(':enter, :leave', ...), /* 2 */ query('.block', style({ opacity: 0 })), /* 3 */ group([ // block executes in parallel query(':enter', [...]), query(':leave', [...]), ]), /* 4 */ query(':enter .block', stagger(400, [ style({ transform: 'translateY(100px)' }), animate('1s ease-in-out', style({ transform: 'translateY(0px)', opacity: 1 })), ])), ]) ])
最终,咱们使用转换(transform)和透明(opacity)来垂直向上滑动进入并淡入。
经过使用交错,咱们为每一个动画引入了一个小的延迟(以毫秒为单位),建立了一个很好的窗帘效果。最后一点,咱们不得不引入一个新的查询,用(opacity:0)初始化.block元素,这样当组件滑入时就不会显示。
做为最终的润色,我添加了一些反向交错离开Home组件的代码而且也加入了贝塞尔曲线来优化路径,最后效果以下图