Search code examples
angularangular9angular-animations

Angular routing component push in/out animation


I'm trying to animate a component to "push itself into" and "get pushed out of" view. Currently, a single container element hosts one component which occupies 100% of the space.

The animation will trigger a second component (living within a router-outlet) to push itself in from the right and share 50% of the space with the first component (which will need to have a smooth animation for the shrink).

Vise versa, the exit animation will look like the first component is "pushing" the second out of view.

Below is the template file for this scenario (assume that the animations have been imported correctly in the component file)

<div class="container">
  <div class="component-1"
    [@triggerComponent1]="outlet.isActivated ? 'shrink' : 'stretch'">
    <component-1></component-1>
  </div>

  <div class="component-2 col-6"
    [@triggerComponent2]="outlet.isActivated ? 'open' : 'close'">
    <router-outlet #outlet="outlet"></router-outlet>
  </div>
</div>

And here are the respective animation trigger functions

export const animation1 = trigger('triggerComponent1', [
  state('stretch', style({
    flex: '0 0 auto',
    width: '100%'
  })),
  state('shrink', style({
    flex: '0 0 auto',
    width: '50%'
  })),
  transition('* => stretch', animate('400ms cubic-bezier(0.35, 0, 0.25, 1)')
  ),
  transition('* => shrink', animate('400ms cubic-bezier(0.35, 0, 0.25, 1)'))
]);

export const animation2 = trigger('triggerComponent2', [
  state('open', style({
    transform: 'translateX(0%)'
  })),
  state('close', style({
    transform: 'translateX(100%)'
  })),
  transition('* => open', animate('400ms cubic-bezier(0.35, 0, 0.25, 1)')
  ),
  transition('* => close', animate('400ms cubic-bezier(0.35, 0, 0.25, 1)'))
]);

The problem with this approach is that the animations are not happening in parallel. Component 1 will smoothly shrink, exposing white space to its right. Then, when the slide animation completes, Component 2 just "appears".

Ideally, I need to combine the animations with a group() but am not sure how to write the trigger nor how I would include it in the template.


Solution

  • Here is a stack blitz for you

    https://stackblitz.com/edit/expanding-drawer-animation?file=src/app/animations.ts

    here is how I did it.

    <div class="container">
       <div class="component-1">
         <div class="content">
              <button (click)="toggle()">expand</button>
              <h1>{{ isExpanded }}</h1>
         </div>
       </div>
       <div class="component-2" [@slideIn]="isExpanded ? 'open' : 'close'">
             <div class="content">
             <h1>Bender?! You stole the atom.</h1>
         </div>
       </div>
     </div>
    

    Then some CSS.

     .container {
       width: 100%;
       max-width: 100%;
       background: red;
       display: flex;
       flex-direction: row;
     }
    
     .content {
       padding: 10px;
     }
    
     .component-1 {
       flex: 1;
       background: rgb(161, 161, 255);
     }
    
     .component-2 {
       flex: 1;
       background: green;
       max-width: 50%;
       overflow: hidden;
     }
    

    The parent is a div flex and the 2 children are 50 / 50 by doing flex:1. Then you can overflow hidden the right div and set the max-width to 50% : 0 based on the variable. we are going to use that to trigger the animation.

      import {
      animate,
      state,
      style,
      transition,
      trigger,
    } from '@angular/animations';
    
    export class Animations {
      static page = [
        trigger('slideIn', [
          state('close', style({ maxWidth: '0%', visibility: 'hidden' })),
          state('open', style({ maxWidth: '50%', visibility: 'visible' })),
          transition('close <=> open', [animate('.6s ease-in-out')]),
          transition(':enter', [
            style({ opacity: 1 }),
            animate('.6s 1.2s ease-in-out'),
          ]),
        ]),
      ];
    }
    

    hope this helps!

    I also did a second one in the stackblitz for you using position absolute and sliding over 50% with padding like I said in my comment below.