typescript 使用 Angular 2.0 路由器和组件接口承诺的页面转换动画

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33553828/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 03:07:08  来源:igfitidea点击:

Page transition animations with Angular 2.0 router and component interface promises

angulartypescriptpromise

提问by SimonHawesome

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:

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

)};

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

产生这样的产品: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...

我想用 Angular 2.0 做一些类似的事情,我觉得我很接近......

So here goes, I've created a simple router in the main application component that controls the navigation between the homeand aboutcomponents.

所以在这里,我在主应用程序组件中创建了一个简单的路由器,用于控制homeabout组件之间的导航。

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: '/'})
])

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 previouscomponent is destroyedI am not able to apply an on leavetransition 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:

似乎有一个使用承诺的解决方案。他们指出 Angular.io 的 API 预览:

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

如果 onDeactivate 返回一个承诺,路由更改将等到承诺解决。

and

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

如果 onActivate 返回一个承诺,则路由更改将等到承诺解决后才能实例化和激活子组件。

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

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 nevergets 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.

我对 Promise 非常陌生,所以我将它混合到我的代码中,这解决了我当前组件在下一个初始化时被销毁的问题,但它永远不会被销毁,它只会创建它的一个新实例。每次我导航回它时,它都会创建一个新实例,从而产生多个副本。

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 businessbefore destroying it and initializing the next component.

所以回顾一下,路由器应该能够等待当前组件完成它的业务,然后销毁它并初始化下一个组件。

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

我希望一切都有意义,我非常感谢您的帮助!

采纳答案by Eric Martinez

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).

正如您从文档中引用的那样,如果这些钩子中的任何一个返回一个 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));
  }

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

请注意,我正在返回一个运行 setTimeout 的 Promise。我们等待一秒钟,让动画有足够的时间完成。

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

我真的不喜欢使用 setTimeouts,所以我们也可以使用 Observables,我个人最喜欢这个。

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.

在这里,我传递了一个随机值(在这种情况下为 true)并延迟它一秒钟,最后将它转换为 Promise。是的,它最终成为一个 Promise,但我不直接使用它。

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

这是一个带有示例工作的plnkr(期望是您正在寻找的)。

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).

PS:如果有时它抱怨找不到 Rx 的路径,请保持刷新直到它起作用(我手动添加了 Rx.js,这对于 plnkr 来说有点沉重)。

回答by Stephen Paul

Angular 2 final solution:

Angular 2 最终解决方案:

plunk

啪啪啪

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

简而言之,我们可以使用@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
        }))
      )
    ])
  ]
})