Angular 4 component responsive animations

10.3k Views Asked by At

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

There are 3 best solutions below

5
On BEST ANSWER

There is no way to define JavaScript behavior based on CSS @Media queries. You will have to do what I did, where I created an Angular appearance service that monitors viewport dimensions and sends commands to components based on what is detected. You can then control animations and appearance with JavaScript logic instead of CSS logic.

Adding an example:

Create a service that monitors viewport width. If the viewport is less than, say, 500px, the service outputs "mobile," else, it outputs "desktop."

In your component, declare one trigger, we'll call it @slide, with multiple states. We will also set a private variable called stateOfYourChoosing, and you can set it to a string.

Then, in your template

<div [@slide]="stateOfYourChoosing"></div>

You can then choose which state you want the default state of stateOfYourChoosing to be, and how you want your component to transition based on component logic.

So, if your appearance service outputs "desktop," your default stateOfYourChoosing would be "slideOut."

transition('slideOut => slideIn', animate('100ms ease-in'))

If the appearance service outputs "mobile," it sets stateOfYourChoosing to "mobileSlideOut," and your transition code is instead

transition('mobileSlideOut => mobileSlideIn', animate('100ms ease-in'))

You can then control all of your responsive animations in your animations module by controlling in what state your trigger is.

0
On

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)
   }
}
0
On

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 ✌