Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular router transition animations slide both left and right conditionally

I have read this article about Router transition Animations for Angular:

https://medium.com/google-developer-experts/angular-supercharge-your-router-transitions-using-new-animation-features-v4-3-3eb341ede6c8

And:

Angular 2 "slide in animation" of a routed component

However, this is too static. I want it to slide left and right depending on the order of the tab.

Is it possible to create router animations for this? Example of what I mean is below:

https://material.angular.io/components/tabs/examples

Look how it slides BOTH left and right very naturally depending on what tab you are on.

This has to be dynamic, because the tabs will be added at runtime.

like image 951
Dolan Avatar asked Feb 22 '18 12:02

Dolan


People also ask

How do you define transition between two states in Angular?

In Angular, transition states can be defined explicitly through the state() function, or using the predefined * (wildcard) and void states.

How do you define transition animation between two states inactive to active )?

Transitions between two states take place so that we can build simple animations between two states driven by a model attribute. Transition basically means navigating from the current state to a new state. In angular, the transition is an animation-specific function which is used in angular's animation DSL language.

What is @routertransition in Angular?

The Angular router comes with high-level animation functions that let you animate the transitions between views when a route changes. To produce an animation sequence when switching between routes, you need to define nested animation sequences.

Which of the following are valid easing functions in Angular animations?

The easing value controls how the animation accelerates and decelerates during its runtime. Value is one of ease , ease-in , ease-out , ease-in-out , or a cubic-bezier() function call. If not supplied, no easing is applied.


2 Answers

I have managed to get this to work by "faking" the state it is in.

In the component.html:

<div [@animRoutes]="pageState">
   <router-outlet></router-outlet>
</div>

pageState is a variable in the component.ts file.

Whenever I click on a tab which I want to go right, I will set pageState to right, and same for left, and let Angular take over the rest.

Note: You have to create a right and right1 state as a hack, because Angular currently does not support right => right state transitions!! Same applies to left of course.

My @Component annotation is below:

@Component({
    selector: 'app-workspace-container',
    templateUrl: './workspace-container.component.html',
    styleUrls: ['./workspace-container.component.scss'],
    animations: [
        trigger('animRoutes', [
            transition('* => right', right),
            transition('* => left', left),
            transition('* => right1', right),
            transition('* => left1', left),
        ]),
    ],
    changeDetection: ChangeDetectionStrategy.OnPush,
})

Where left, left1, right, right1 are:

const left = [
    query(':enter, :leave', style({ position: 'fixed', width: '100%' }), { optional: true }),
    group([
        query(':enter', [style({ transform: 'translateX(-100%)' }), animate('.3s ease-out', style({ transform: 'translateX(0%)' }))], {
            optional: true,
        }),
        query(':leave', [style({ transform: 'translateX(0%)' }), animate('.3s ease-out', style({ transform: 'translateX(100%)' }))], {
            optional: true,
        }),
    ]),
];

const right = [
    query(':enter, :leave', style({ position: 'fixed', width: '100%' }), { optional: true }),
    group([
        query(':enter', [style({ transform: 'translateX(100%)' }), animate('.3s ease-out', style({ transform: 'translateX(0%)' }))], {
            optional: true,
        }),
        query(':leave', [style({ transform: 'translateX(0%)' }), animate('.3s ease-out', style({ transform: 'translateX(-100%)' }))], {
            optional: true,
        }),
    ]),
];

TL;DR: Make the state you are going to into a variable, so you can dynamically set the state which you wish you are going to.

like image 165
Dolan Avatar answered Sep 21 '22 09:09

Dolan


Today things are a bit simpler because new animation aliases exist as :increment and :decrement. Aliases have been introduced in Angular 5.

So my modified solution is:

    @Component({
      selector: 'app-workspace-container',
      templateUrl: './workspace-container.component.html',
      styleUrls: ['./workspace-container.component.scss'],
      animations: [
        trigger('animRoutes', [
          transition(':increment', right),
          transition(':decrement', left),
        ]),
      ],
  })
  export class ComponentContainingRouterOutlet implements OnDestroy, OnInit {
    //... ngOnInit,ngOnDestroy

    constructor( private route: ActivatedRoute ) { }

    animationState: number;

    onActivate($event) {
      this.animationState = this.route.firstChild.snapshot.data['routeIdx'];
    }
  }

Call animation at router-outlet position:

<div [@animRoutes]="animationState">
  <router-outlet (activate)="onActivate($event)"></router-outlet>
</div>

modify routes definition as example, look at data: { routeIdx: X } :

    const routes: Routes = [
      {
        path: 'routeOne',
        component: ComponentOne,
        data: { routeIdx: 0 }
      },
      {
        path: 'routeTwo',
        component: ComponentTwo,
        data: { routeIdx: 1}
      },
      {
        path: 'routeThree',
        component: ComponentThree,
        data: { routeIdx: 2 }
      },
      {
        path: 'routeFour',
        component: ComponentFour,
        data: { routeIdx: 3 }
      },
      {
        path: '',
        redirectTo: 'routeOne',
        pathMatch: 'full'
      }
    ]

And transitions are the same as in Dolan's post:

const left = [
    query(':enter, :leave', style({ position: 'fixed', width: '100%' }), { optional: true }),
    group([
        query(':enter', [style({ transform: 'translateX(-100%)' }), animate('.3s ease-out', style({ transform: 'translateX(0%)' }))], {
            optional: true,
        }),
        query(':leave', [style({ transform: 'translateX(0%)' }), animate('.3s ease-out', style({ transform: 'translateX(100%)' }))], {
            optional: true,
        }),
    ]),
];

const right = [
    query(':enter, :leave', style({ position: 'fixed', width: '100%' }), { optional: true }),
    group([
        query(':enter', [style({ transform: 'translateX(100%)' }), animate('.3s ease-out', style({ transform: 'translateX(0%)' }))], {
            optional: true,
        }),
        query(':leave', [style({ transform: 'translateX(0%)' }), animate('.3s ease-out', style({ transform: 'translateX(-100%)' }))], {
            optional: true,
        }),
    ]),
];
like image 28
Michal.S Avatar answered Sep 21 '22 09:09

Michal.S