Подтвердить что ты не робот

Анимации с переходом страницы с помощью Angular 2.0 маршрутизатора и интерфейса компонента 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, и мне кажется, что я довольно близко...

Итак, вот, я создал простой маршрутизатор в основном компоненте приложения, который управляет навигацией между домом и компонентами.

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

В настоящий момент я могу зафиксировать, когда маршрутизатор создавал экземпляр или уничтожал конкретный компонент при переходе от одного к другому. Это здорово, но когда предыдущий компонент уничтожен, я не могу применить анимацию перехода на выходе до того, как будет инициализирован следующий компонент.

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. API Предварительный просмотр API:

Если onDeactivate возвращает обещание, изменение маршрута будет ждать, пока не будет достигнуто обещание.

и

Если onActivate возвращает обещание, изменение маршрута будет ждать, пока обещание не решит создать экземпляр и не активировать дочерние компоненты.

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

Я абсолютно новый для promises, поэтому я объединил это вместе в свой код, который решил проблему уничтожения моего текущего компонента при инициализации следующего, но затем он никогда не разрушается, он создает только новый экземпляр из этого. Каждый раз, когда я вернусь к нему, он создаст новый экземпляр, в результате получится несколько копий.

onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {

    function ani(){
      TweenMax.fromTo($(".title"), 1, {opacity: 1}, {opacity: 0});
    }

    var aniPromise = ani();

    aniPromise.then(function (ani) {
        ani();
    });

}

Итак, чтобы повторить, маршрутизатор должен уметь ждать, пока текущий компонент завершит его бизнес, прежде чем уничтожить его и инициализировать следующий компонент.

Я надеюсь, что все имеет смысл, и я очень ценю помощь!

4b9b3361

Ответ 1

Как вы указали в документах, если какой-либо из этих крючков возвращает 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));
  }

Обратите внимание, что я возвращаю Promise, который запускает setTimeout. Мы подождаем секунду, чтобы дать время анимации, достаточное для завершения.

Мне не очень нравится использование setTimeouts, поэтому мы можем использовать Observables, что лично мне нравится лучше всего.

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

Здесь я передаю случайное значение (правда в этом случае) и задерживаю его на секунду и, наконец, передаю его в Promise. Да, это заканчивается обещанием, но я не использую его напрямую.

Здесь plnkr с примером работы (ожидающим того, что вы ищете).

PS: Если иногда он жалуется на то, что не может найти путь к Rx, просто продолжайте обновляться до тех пор, пока он не будет работать (я добавил Rx.js вручную, и он немного тяжелый для plnkr).

Ответ 2

Angular 2 окончательное решение:

plunk

В двух словах мы можем использовать встроенную директиву @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
        }))
      )
    ])
  ]
})