Angular 4 component responsive animations

Viewed 9481

I am working on an Angular responsive app, which on mobile it has a drawer.

I love Web Animations API implementation in Angular, but I can't find a place where I can configure animations based on my media-queries breakpoints.

All I can find is canceling the animations via my css sheet, but thats make me start to spread the code on different places in my project, and I'm not sure that this is what angular intended for me to do...

Real life example

My application drawer uses this code to animate

<div class="mobile-menu" [@animateDrawer]="drawerOpened">
  <!-- Drawer's menu goes here -->
</div>

drawerOpened is a boolean that toggle when app menu button is pressed.

My component looks like that:

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss'],
  animations: [
    trigger('animateDrawer', [
      state('inactive', style({
        transform: 'translate3d(-100%, 0, 0)'
      })),
      state('active', style({
        transform: 'translate3d(0, 0, 0)'
      }))
    ])
  ]
})

The CSS code where I cancel the animations effects

.mobile-menu {
  opacity: 1 !important;
  display: flex !important;
  transform: none !important;
}

Besides manipulating all in my css code & disabling the css properties on Desktop BreakPoint, does exist some way to do it inside Angular context?

Thank you!

3 Answers

Not the most robust solutions, but if the end state of the animation is the same for each media query (e.g. you are animating both items in to view but want different animations for mobile and desktop), then you can define the initial states in css using media queries and in the angular component use '*' for the target css properties initial state

trigger('animation', [
  state('out, none, void', style({
    transform: '*'
  })),
  state('in', style({
    transform: 'translate3d(0, 0, 0)'
  })),
  transition('below => in, none => in', animate(`300ms ease`))
])

then in the css

.class {
  //animate from bottom on mobile
  transform: translate3d(0, -100%, 0);

   @media all and (min-width: 920px) {
     //animate from left on desktop
     transform: translate3d(-100%, 0, 0)
   }
}

One way would be to create your animation on runtime instead of declaring them in your component decorator.

  1. Create a service that will be used by components.
  2. Create a new method that received animation configuration.
  3. Detect window size.
  4. Build and return the animation.

It would look somewhat like this (The following code is not tested and is simply an example):

export interface MediaQueryStyle
{
    minWidth: number;
    initialStyle: any; // Style object to apply before animation
    endStyle: any;     // Style object to apply after animation
}

export interface MediaQueryAnimationConfig
{
    element: ElementRef;
    mediaStyles: MediaQueryStyle[];
    duration?: number | string;

    ... // Whatever you need to create your animation 
}

The service:

@Injectable({
    providedIn: 'root'
})
export class MediaQueryAnimationService
{
    constructor(private builder: AnimationBuilder) { }

    public create(config: MediaQueryAnimationConfig): AnimationPlayer
    {
        // Read animation configuration
        const duration = config.duration || 1000;
        const mediaStyle = this.findMediaStyle(config.styles);

        // Build the animation logic (add here any other operation, e.g. query)
        const animation = this.builder.build([
            style(mediaStyle.initialStyle),
            animate(duration, style(mediaStyle.endStyle)
        ]);

        return animation.create(config.element);
    }

    private findMediaStyle(styles: MediaQueryStyle[])
    {
        const viewWidth = window.innerWidth;

        // Some logic to scan the array and return the style that complies with `viewWidth`
        return styles.find(style => ...);
    }
}

In your component:

<something ... #someElement></something>
@Component({
    selector: 'something',
    templateUrl: './something.component.html',
    styleUrls: ['./something.component.scss']
})
export class SomethingComponent implements OnInit
{
    @ViewChild('someElement')
    private elementRef: ElementRef;

    constructor(private mqAnimation: MediaQueryAnimationService) { }

    ngOnInit() { ... }

    public onSomeEvent()
    {
        const player = mqAnimation.create({ element: elementRef.element, minWidth: ... });

        player.onDone(player.destroy);

        player.play();
    }
}

Useful reading and examples:

https://angular.io/api/animations/AnimationBuilder

https://stackblitz.com/edit/angular-animation-builder

Animation inside of MatDialog is not working

Good luck ✌

Related