Page transition animations with Angular 2.0 router and component interface promises

匿名 (未验证) 提交于 2019-12-03 03:05:02

问题:

In Angular 1.x we can use ngAnimate to detect when we are leaving or entering a particular route. Furthermore we are able to apply behaviors to them:

animateApp.animation('.myElement', function(){      return {          enter : function(element, done) {             //Do something on enter         },          leave : function(element, done) {             //Do something on leave         }     };  )};

Resulting in a product like this: http://embed.plnkr.co/uW4v9T/preview

I would like to do something similar with Angular 2.0 and I feel like I'm pretty close...

So here goes, I've created a simple router in the main application component that controls the navigation between the home and about components.

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: `     

Home Page

` }) 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: `

About Page

` }) 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: `

Hello {{message}}!

home about
`, 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: '/'}) ])

At the moment I am able to capture when the router has instantiated or destroyed a particular component when it moves from one to the next. This is great, but when the the previous component is destroyed I am not able to apply an on leave transition animation before the next component is initialized.

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});     }  }

It seems like there is a solution to this using promises. Angular.io's API preview they state:

If onDeactivate returns a promise, the route change will wait until the promise settles.

and

If onActivate returns a promise, the route change will wait until the promise settles to instantiate and activate child components.

https://angular.io/docs/ts/latest/api/

I am super brand new to promises so I mashed this together into my code which solved the problem of my current component being destroyed on initialization of the next one, but then it never gets destroyed, it only creates a new instance of it. Every time I navigate back to it, it will create a new instance resulting in multiple copies.

onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {      function ani(){       TweenMax.fromTo($(".title"), 1, {opacity: 1}, {opacity: 0});     }      var aniPromise = ani();      aniPromise.then(function (ani) {         ani();     });  }

So to recap, the router should be able to wait for the current component to finish it's business before destroying it and initializing the next component.

I hope that all makes sense and I really appreciate the help!

回答1:

As you quoted from the docs, if any of this hooks returns a Promise it will wait until its completed to move to the next one, so you can easily return a Promise that basically does nothing and wait a second (or as many time as you need).

 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));   }

Note that I'm returning a Promise which runs a setTimeout. We wait a second to give the animation time enough to be completed.

I don't really like using setTimeouts, so we can use Observables as well, that personally I like the best.

return Rx.Observable.of(true).delay(1000).toPromise();

Here I'm passing a random value (true in this case) and delay it a second and finally cast it to Promise. Yes, it ends up being a Promise but I don't use it directly.

Here's a plnkr with an example working (expecting to be what you are looking for).

PS: If sometimes it complains about that it can't find a path to Rx, just keep refreshing until it works (I added Rx.js manually and it's a little heavy for plnkr apprently).



回答2:

Angular 2 final solution:

plunk

In a nutshell, we can to use the @routeAnimation built-in directive to achieve this. Each of our components representing a child route will be decorated with something like:

@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         }))       )     ])   ] })


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!