Angular 2.0 路由器和组件接口承诺的页面转换动画
Page transition animations with Angular 2.0 router and component interface promises
在 Angular 1.x 中,我们可以使用 ngAnimate 来检测我们何时离开或进入特定路线。此外,我们还可以对它们应用行为:
animateApp.animation('.myElement', function(){
return {
enter : function(element, done) {
//Do something on enter
},
leave : function(element, done) {
//Do something on leave
}
};
)};
产生这样的产品:http://embed.plnkr.co/uW4v9T/preview
我想用 Angular 2.0 做一些类似的事情,我觉得我很接近...
那么,我在主应用程序组件中创建了一个简单的路由器,用于控制 home 和 about 之间的导航组件。
import { bootstrap, bind, Component, provide, View } from 'angular2/angular2';
import {RouteConfig, RouteParams, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, APP_BASE_HREF, ROUTER_BINDINGS} from 'angular2/router'
/////////////////////////////////////////////////////////////////
// Home Component Start
/////////////////////////////////////////////////////////////////
@Component({
selector: 'home-cmp'
})
@View({
template: `
<h2 class="title">Home Page</h2>
`
})
class HomeCmp implements OnActivate, onDeactivate{
onActivate(next: ComponentInstruction, prev: ComponentInstruction) {
console.log("Home Page - initialized");
}
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
console.log("Home Page - destroyed");
}
}
/////////////////////////////////////////////////////////////////
// Home Component End
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// About Component Start
/////////////////////////////////////////////////////////////////
@Component({
selector: 'about-cmp'
})
@View({
template: `
<h2 class="title">About Page</h2>
`
})
class AboutCmp implements OnActivate, onDeactivate {
onActivate(next: ComponentInstruction, prev: ComponentInstruction) {
console.log("About Page - initialized");
}
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
console.log("About Page - destroyed");
}
}
/////////////////////////////////////////////////////////////////
// About Component End
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// Main Application Componenent Start
/////////////////////////////////////////////////////////////////
@Component({
selector: 'my-app'
})
@View({
template: `
<div>
<h1>Hello {{message}}!</h1>
<a [router-link]="['./HomeCmp']">home</a>
<a [router-link]="['./AboutCmp']">about</a>
<hr>
<router-outlet></router-outlet>
</div>
`,
directives: [ROUTER_DIRECTIVES]
})
@RouteConfig([
{path: '/', component: HomeCmp, as: 'HomeCmp'},
{path: '/about', component: AboutCmp, as: 'AboutCmp'}
])
export class App {
}
/////////////////////////////////////////////////////////////////
// Main Application Componenent End
/////////////////////////////////////////////////////////////////
bootstrap(App, [
ROUTER_BINDINGS,
ROUTER_PROVIDERS,
ROUTER_DIRECTIVES,
provide(APP_BASE_HREF, {useValue: '/'})
])
目前,当路由器从一个组件移动到另一个组件时,我能够捕获它何时实例化或销毁特定组件。这很好,但是当 previous 组件被 destroyed 时,我无法申请 on leave 下一个组件初始化之前的过渡动画。
class HomeCmp implements OnActivate, onDeactivate{
onActivate(next: ComponentInstruction, prev: ComponentInstruction) {
//This works
TweenMax.fromTo($(".title"), 1, {opacity: 0}, {opacity: 1});
}
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
//This get ignored
TweenMax.fromTo($(".title"), 1, {opacity: 0}, {opacity: 1});
}
}
似乎有一个使用 promises 的解决方案。 Angular.io 的 API 预览他们说:
如果 onDeactivate returns 一个 promise,路由更改将等到 promise 完成。
和
如果 onActivate returns 一个 promise,路由更改将等到 promise 解决以实例化和激活子组件。
https://angular.io/docs/ts/latest/api/
我是 promises 的超级新手,所以我将其整合到我的代码中,解决了我当前组件在下一个组件初始化时被破坏的问题,但是 never 被销毁,它只会创建它的一个新实例。每次我导航回它时,它都会创建一个新实例,从而产生多个副本。
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
function ani(){
TweenMax.fromTo($(".title"), 1, {opacity: 1}, {opacity: 0});
}
var aniPromise = ani();
aniPromise.then(function (ani) {
ani();
});
}
所以回顾一下,路由器应该能够等待当前组件完成它的业务,然后再销毁它并初始化下一个组件。
我希望一切都有意义,非常感谢您的帮助!
正如您从文档中引用的那样,如果其中任何一个钩子 return 是一个 Promise,它将等到它完成后才移动到下一个,因此您可以轻松地 return 一个 Promise,基本上什么也不做,稍等片刻(或您需要的时间)。
onActivate(next: ComponentInstruction, prev: ComponentInstruction) {
TweenMax.fromTo($(".title"), 1, {opacity: 0}, {opacity: 1});
return new Promise((res, rej) => setTimeout(() => res(1), 1000));
}
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
TweenMax.fromTo($(".title"), 1, {opacity:1}, {opacity: 0});
return new Promise((res, rej) => setTimeout(() => res(1), 1000));
}
请注意,我正在 return 运行 setTimeout 的 Promise。我们稍等片刻,让动画有足够的时间完成。
我不太喜欢使用 setTimeouts,所以我们也可以使用 Observables,我个人最喜欢。
return Rx.Observable.of(true).delay(1000).toPromise();
在这里,我传递了一个随机值(在本例中为 true)并将其延迟一秒钟,最后将其转换为 Promise。是的,它最终成为一个 Promise,但我不直接使用它。
这里有一个 plnkr,其中包含一个有效的示例(期望是您要查找的内容)。
PS:如果有时它抱怨找不到Rx的路径,就一直刷新直到它起作用(我手动添加了Rx.js,这对plnkr来说有点重).
Angular2最终解法:
简而言之,我们可以使用@routeAnimation
内置指令来实现这一点。我们代表子路由的每个组件都将用类似的东西装饰:
@Component({
selector: 'app-pageone'
host: { '[@routeAnimation]': 'true' },
styles: [':host { width: 300px; display: block; position: absolute; }']
animations: [
trigger('routeAnimation', [
state('*', style({transform: 'translateX(0)', opacity: 1})),
transition('void => *', [
style({transform: 'translateX(-100%)', opacity: 0}),
animate('0.5s cubic-bezier(0.215, 0.610, 0.355, 1.000)')
]),
transition('* => void',
animate('0.5s cubic-bezier(0.215, 0.610, 0.355, 1.000)', style({
transform: 'translateX(100%)',
opacity: 0
}))
)
])
]
})
在 Angular 1.x 中,我们可以使用 ngAnimate 来检测我们何时离开或进入特定路线。此外,我们还可以对它们应用行为:
animateApp.animation('.myElement', function(){
return {
enter : function(element, done) {
//Do something on enter
},
leave : function(element, done) {
//Do something on leave
}
};
)};
产生这样的产品:http://embed.plnkr.co/uW4v9T/preview
我想用 Angular 2.0 做一些类似的事情,我觉得我很接近...
那么,我在主应用程序组件中创建了一个简单的路由器,用于控制 home 和 about 之间的导航组件。
import { bootstrap, bind, Component, provide, View } from 'angular2/angular2';
import {RouteConfig, RouteParams, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, APP_BASE_HREF, ROUTER_BINDINGS} from 'angular2/router'
/////////////////////////////////////////////////////////////////
// Home Component Start
/////////////////////////////////////////////////////////////////
@Component({
selector: 'home-cmp'
})
@View({
template: `
<h2 class="title">Home Page</h2>
`
})
class HomeCmp implements OnActivate, onDeactivate{
onActivate(next: ComponentInstruction, prev: ComponentInstruction) {
console.log("Home Page - initialized");
}
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
console.log("Home Page - destroyed");
}
}
/////////////////////////////////////////////////////////////////
// Home Component End
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// About Component Start
/////////////////////////////////////////////////////////////////
@Component({
selector: 'about-cmp'
})
@View({
template: `
<h2 class="title">About Page</h2>
`
})
class AboutCmp implements OnActivate, onDeactivate {
onActivate(next: ComponentInstruction, prev: ComponentInstruction) {
console.log("About Page - initialized");
}
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
console.log("About Page - destroyed");
}
}
/////////////////////////////////////////////////////////////////
// About Component End
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// Main Application Componenent Start
/////////////////////////////////////////////////////////////////
@Component({
selector: 'my-app'
})
@View({
template: `
<div>
<h1>Hello {{message}}!</h1>
<a [router-link]="['./HomeCmp']">home</a>
<a [router-link]="['./AboutCmp']">about</a>
<hr>
<router-outlet></router-outlet>
</div>
`,
directives: [ROUTER_DIRECTIVES]
})
@RouteConfig([
{path: '/', component: HomeCmp, as: 'HomeCmp'},
{path: '/about', component: AboutCmp, as: 'AboutCmp'}
])
export class App {
}
/////////////////////////////////////////////////////////////////
// Main Application Componenent End
/////////////////////////////////////////////////////////////////
bootstrap(App, [
ROUTER_BINDINGS,
ROUTER_PROVIDERS,
ROUTER_DIRECTIVES,
provide(APP_BASE_HREF, {useValue: '/'})
])
目前,当路由器从一个组件移动到另一个组件时,我能够捕获它何时实例化或销毁特定组件。这很好,但是当 previous 组件被 destroyed 时,我无法申请 on leave 下一个组件初始化之前的过渡动画。
class HomeCmp implements OnActivate, onDeactivate{
onActivate(next: ComponentInstruction, prev: ComponentInstruction) {
//This works
TweenMax.fromTo($(".title"), 1, {opacity: 0}, {opacity: 1});
}
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
//This get ignored
TweenMax.fromTo($(".title"), 1, {opacity: 0}, {opacity: 1});
}
}
似乎有一个使用 promises 的解决方案。 Angular.io 的 API 预览他们说:
如果 onDeactivate returns 一个 promise,路由更改将等到 promise 完成。
和
如果 onActivate returns 一个 promise,路由更改将等到 promise 解决以实例化和激活子组件。
https://angular.io/docs/ts/latest/api/
我是 promises 的超级新手,所以我将其整合到我的代码中,解决了我当前组件在下一个组件初始化时被破坏的问题,但是 never 被销毁,它只会创建它的一个新实例。每次我导航回它时,它都会创建一个新实例,从而产生多个副本。
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
function ani(){
TweenMax.fromTo($(".title"), 1, {opacity: 1}, {opacity: 0});
}
var aniPromise = ani();
aniPromise.then(function (ani) {
ani();
});
}
所以回顾一下,路由器应该能够等待当前组件完成它的业务,然后再销毁它并初始化下一个组件。
我希望一切都有意义,非常感谢您的帮助!
正如您从文档中引用的那样,如果其中任何一个钩子 return 是一个 Promise,它将等到它完成后才移动到下一个,因此您可以轻松地 return 一个 Promise,基本上什么也不做,稍等片刻(或您需要的时间)。
onActivate(next: ComponentInstruction, prev: ComponentInstruction) {
TweenMax.fromTo($(".title"), 1, {opacity: 0}, {opacity: 1});
return new Promise((res, rej) => setTimeout(() => res(1), 1000));
}
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
TweenMax.fromTo($(".title"), 1, {opacity:1}, {opacity: 0});
return new Promise((res, rej) => setTimeout(() => res(1), 1000));
}
请注意,我正在 return 运行 setTimeout 的 Promise。我们稍等片刻,让动画有足够的时间完成。
我不太喜欢使用 setTimeouts,所以我们也可以使用 Observables,我个人最喜欢。
return Rx.Observable.of(true).delay(1000).toPromise();
在这里,我传递了一个随机值(在本例中为 true)并将其延迟一秒钟,最后将其转换为 Promise。是的,它最终成为一个 Promise,但我不直接使用它。
这里有一个 plnkr,其中包含一个有效的示例(期望是您要查找的内容)。
PS:如果有时它抱怨找不到Rx的路径,就一直刷新直到它起作用(我手动添加了Rx.js,这对plnkr来说有点重).
Angular2最终解法:
简而言之,我们可以使用@routeAnimation
内置指令来实现这一点。我们代表子路由的每个组件都将用类似的东西装饰:
@Component({
selector: 'app-pageone'
host: { '[@routeAnimation]': 'true' },
styles: [':host { width: 300px; display: block; position: absolute; }']
animations: [
trigger('routeAnimation', [
state('*', style({transform: 'translateX(0)', opacity: 1})),
transition('void => *', [
style({transform: 'translateX(-100%)', opacity: 0}),
animate('0.5s cubic-bezier(0.215, 0.610, 0.355, 1.000)')
]),
transition('* => void',
animate('0.5s cubic-bezier(0.215, 0.610, 0.355, 1.000)', style({
transform: 'translateX(100%)',
opacity: 0
}))
)
])
]
})