Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Dialog] Allow customization of open/close animations #8857

Closed
Howard-Song opened this issue Dec 7, 2017 · 46 comments
Closed

[Dialog] Allow customization of open/close animations #8857

Howard-Song opened this issue Dec 7, 2017 · 46 comments
Assignees
Labels
area: material/dialog feature This issue represents a new feature or feature request rather than a bug or bug fix needs: discussion Further discussion with the team is needed before proceeding P5 The team acknowledges the request but does not plan to address it, it remains open for discussion

Comments

@Howard-Song
Copy link

Bug, feature request, or proposal:

feature

What is the expected behavior?

When open or close dialog,animation should define by developer.

What is the current behavior?

I found that open and close dialog <mat-dialog-container></mat-dialog-container>will add class ng-animating,well open will add once and remove within a short time period(less than 0.5s),but in my case,open and close will use different animation,so it doesn't work for me to overwrite .ng-animating

What is the use-case or motivation for changing an existing behavior?

Add different animation in open and close dialog.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Angular: 5.0.0,
Material: 5.0.0-rc0,
OS: win10,
TypeScript: 2.6.1,
Browsers: Chrome@62.0.3202.94

@swftvsn
Copy link
Contributor

swftvsn commented Dec 7, 2017

This should actually be implemented to all components that use animations.

A consistent way to override animations: by default provide animations that comply with material spec, but let developers override those with custom ones.

@andrewseguin andrewseguin changed the title Change dialog open and close with two different animation [Dialog] Allow customization of open/close animations Dec 12, 2017
@andrewseguin andrewseguin added feature This issue represents a new feature or feature request rather than a bug or bug fix P4 A relatively minor issue that is not relevant to core functions labels Dec 12, 2017
@jelbourn jelbourn added P5 The team acknowledges the request but does not plan to address it, it remains open for discussion discussion and removed P4 A relatively minor issue that is not relevant to core functions labels Dec 12, 2017
@bkandoori
Copy link

This is a very useful feature , it will be great if we can add the support.

@marc-wilson
Copy link

marc-wilson commented Apr 8, 2018

This would be an extremely nice feature. I've hit a fork in the road with a project because I need much more flexibility out of material for simple things such as which direction a dialog flys out from. I am reading about the CDK which is pretty light right now, but it seems to offer something. I am just not sure yet (heavily lacking in documentation). The other option I see is to use the NoopAnimationsModule which kills the animation completely but that doesn't seem to get me anywhere either. It's an all or nothing thing. It doesn't look like you can do it on a per-component-basis.

@ayyazzafar
Copy link

We are looking forward to have this feature available.
This would be very helpful.
Thanks

@angular angular deleted a comment from gowebvision Jun 11, 2018
@omieliekh
Copy link

Since there is no way (yet) to override animations officially, there is always dirty hack that will allow you to do that (example is for MatMenu, but works for other elements):

import { MatMenu } from '@angular/material';

import {
  animate,
  state,
  style,
  transition,
  trigger
} from '@angular/animations';

MatMenu['decorators'][0].args[0].animations[0] = trigger('transformMenu', [
  state('void', style({
    opacity: 0,
    transform: 'scale(0, 0)'
  })),
  state('enter-start', style({
    opacity: 0,
    transform: 'scale(0, 0)'
  })),
  state('enter', style({
    opacity: 1,
    transform: 'scale(1, 1)'
  })),
  transition('void => enter-start', animate('0ms ease-in-out')),
  transition('enter-start => enter', animate('300ms ease-in-out')),
  transition('* => void', animate('300ms ease-in-out', style({ opacity: 0 })))
]);

This code for example, replaces one of two angular-animations of MatMenu element, dropdown show/hide animation.
It has some limitations - animation name should be the same as original one (transformMenu) as well as states...

But coupled with property checks it can be quite long-living solution.

To find needed animation's name and its states, you can to go to the source (in my case node_modules/@angular/material/esm2015/menu.js) and search for animations there.

@chipallen2
Copy link

I also need the ability to edit the animations on a dialog. Please make this configurable.

@omieliekh I tried your code but couldn't get it to work for MatDialog. I see what you referenced in node_modules/@angular/material/esm2015/dialog.js on line 94 but I don't follow how I can edit those state transitions for a single instance of a dialog?

Can you share how you did that?

@omieliekh
Copy link

@chipallen2 sorry for frustration caused, unfortunately my code is not working on production 🙁
I'm relying on fact that animations property should exist, but somehow it doesn't exists for minified object. At the end of the day I didn't find a way to override the animation...

@carvarr
Copy link

carvarr commented Jul 13, 2018

You can try to override material classes.

This slides down the dialog.

@Keyframes slideDown{
0%{ }
100%{
transform: translateY(20%);
}
}

.cdk-overlay-pane{
transform: translateY(-250%);
animation: slideDown 0.5s forwards 0s ease-in;
}
.cdk-overlay-container > * {
transition: none;
}

You can see it in action here: https://angular-je6ade.stackblitz.io

@FirstVertex
Copy link

@carvarr it works great to customize the show animation, but how can we customize the hide animation?

@Gauravseta
Copy link

so custom animation working on dialogs? i dont see it yet working. can anyone pplease clarify final status of this issue?

@Jiish
Copy link

Jiish commented Sep 25, 2018

Upvoting this feature.

@carvarr 's suggestion works nicely for animating dialog entry. @omieliekh 's solution allows full customization of the dialog animations, however this always applies globally to all dialogs and I could not get this solution to work only on one instance of a material dialog.

@liesahead
Copy link

Any news on that one? Will that become available?

@sashanana
Copy link

@omieliekh 's solution doesn't work on --prod, or did i miss something?

@sashanana
Copy link

My work around based on solution of @omieliekh, no error on build --prod, it is working for the wait of the feature release.

`export interface AnimationDefination {
keyframes: ɵStyleData[],
duration: number,
delay: number,
easing: string
}

export type RedefinedAnimationCallback = (AnimationDefination) => AnimationDefination;

export class ComponentDependentAnimationDriver extends ɵWebAnimationsDriver {

private redefindAnimations: { [key: string]: RedefinedAnimationCallback } = {};
/**
 * Override ɵWebAnimationsDriver with animation by component
 */
public animate(
    element: any,
    keyframes: ɵStyleData[],
    duration: number,
    delay: number,
    easing: string,
    previousPlayers: AnimationPlayer[] = []
): any {

    const defination = this.getRedefindAnimationByTagName(
        element.tagName,
        {
            keyframes,
            duration,
            delay,
            easing
        }
    );
    return super.animate(element, defination.keyframes, defination.duration, defination.delay, defination.easing, previousPlayers);

}

/**
 * Register callback to redefine animation by component
 */
public registerRedefinedAnimationCallback(
    tagName: string,
    callback: RedefinedAnimationCallback
): void {
    this.redefindAnimations[tagName] = callback;
}

/**
 *
 * Get redefination animation by component
 * @private
 * @param {*} tagName
 * @param {AnimationDefination} defination
 * @returns {AnimationDefination}
 * @memberof ComponentDependentAnimationDriver
 */
private getRedefindAnimationByTagName(
    tagName: any,
    defination: AnimationDefination
): AnimationDefination { 

    if (this.redefindAnimations[tagName]) {
        defination = this.redefindAnimations[tagName](defination);
    }

    return defination;

}

}`

In app modules

providers: [ { provide: AnimationDriver, useClass: ComponentDependentAnimationDriver }, { provide: APP_INITIALIZER, useFactory: redefineDialogAnimation, deps: [ AnimationDriver ], multi: true } ]
export function redefineDialogAnimation(animationDriver: AnimationDriver): () => Promise<any> { return () => new Promise((resolve, reject) => { if (animationDriver && animationDriver['registerRedefinedAnimationCallback']) { // // Register redefine animation // animationDriver['registerRedefinedAnimationCallback']( 'MAT-DIALOG-CONTAINER', (defination: AnimationDefination) => { const keyframes = defination.keyframes; if (keyframes && keyframes.length === 2) { // // Valid animation to redefine // if (keyframes[0].opacity === '0') { /* Active */ keyframes[0] = { easing, ...deactiveStyle }; keyframes[1] = activeStyle; } else if (keyframes[0].opacity === '1') { /* Deactive */ keyframes[0] = { easing, ...activeStyle }; keyframes[1] = deactiveStyle; } } defination.duration = 200; return defination; } ); // End Register } resolve(); }); }

@vladbash
Copy link

Since there is no way (yet) to override animations officially, there is always dirty hack that will allow you to do that (example is for MatMenu, but works for other elements):

import { MatMenu } from '@angular/material';

import {
  animate,
  state,
  style,
  transition,
  trigger
} from '@angular/animations';

MatMenu['decorators'][0].args[0].animations[0] = trigger('transformMenu', [
  state('void', style({
    opacity: 0,
    transform: 'scale(0, 0)'
  })),
  state('enter-start', style({
    opacity: 0,
    transform: 'scale(0, 0)'
  })),
  state('enter', style({
    opacity: 1,
    transform: 'scale(1, 1)'
  })),
  transition('void => enter-start', animate('0ms ease-in-out')),
  transition('enter-start => enter', animate('300ms ease-in-out')),
  transition('* => void', animate('300ms ease-in-out', style({ opacity: 0 })))
]);

This code for example, replaces one of two angular-animations of MatMenu element, dropdown show/hide animation.
It has some limitations - animation name should be the same as original one (transformMenu) as well as states...
But coupled with property checks it can be quite long-living solution.
To find needed animation's name and its states, you can to go to the source (in my case node_modules/@angular/material/esm2015/menu.js) and search for animations there.

it doest work to dialog.

sorry, but could you fix your typo in doest? tnx

@Jiish
Copy link

Jiish commented Mar 27, 2019

Upvoting this feature.
@carvarr 's suggestion works nicely for animating dialog entry. @omieliekh 's solution allows full customization of the dialog animations, however this always applies globally to all dialogs and I could not get this solution to work only on one instance of a material dialog.

@Jiish were you able to get this working with --prod?

Unfortunately this solution never got past ”ng serve”, so I can’t say whether it would work with minified code or not. Still waiting for official solution, before applying custom animations to production code.

@atljoseph
Copy link

would be nice to have this

@pburkindine
Copy link

Upvote

@BelvedereHenrique
Copy link

Any updates on this?

@Ahmdrza
Copy link

Ahmdrza commented Sep 19, 2019

Angular material is super slow. Tried all workarounds but none is working.

@matze1234
Copy link

matze1234 commented Sep 30, 2019

For MatDialog it's in here:
MatDialogContainer['decorators'][0].args[0].animations[0];
I changed it within main.ts to have effect.

@yantrab
Copy link

yantrab commented Oct 11, 2019

My custom dialog service, with animation and more.

@SharanSMenon
Copy link

When will the CDKDialog be released?

@14asdf
Copy link

14asdf commented Nov 27, 2019

You can try to override material classes.

This slides down the dialog.

@Keyframes slideDown{
0%{ }
100%{
transform: translateY(20%);
}
}

.cdk-overlay-pane{
transform: translateY(-250%);
animation: slideDown 0.5s forwards 0s ease-in;
}
.cdk-overlay-container > * {
transition: none;
}

You can see it in action here: https://angular-je6ade.stackblitz.io

My custom dialog service, with animation and more.

works in chrome and firefox but does not work in safari

@mmalerba mmalerba added needs: discussion Further discussion with the team is needed before proceeding and removed discussion labels Mar 3, 2020
@omaracrystal
Copy link

omaracrystal commented Jun 4, 2020

So the way I worked around this was to add a panel-class to the dialog then set up custom animation for that dialog.

global scss file

//--------------------------------------------
// DIALOG SIDE PANEL
//--------------------------------------------
@keyframes slide {
  100% { right: 0; }
}

.dialog-side-panel {
  position: fixed !important;
  bottom: 0;
  top: 0;
  right: -100vw;
  width: 100vw;
  height: 100%;
  animation: slide 0.1s forwards;
  animation-delay: 0.1s;

  .mat-dialog-container {
    border-radius: 0;
  }
}

custom dialog button component

import { Component, Input, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Overlay } from '@angular/cdk/overlay';
import { SidePanelRadioFilterComponent } from '@enterprise/prod/shared/filters/side-panel-radio-filter/side-panel-radio-filter.component';
import { SelectOption } from '@enterprise/prod/shared/filters/select/select-options';

@Component({
  selector: 'senet-button-filter',
  templateUrl: './button-filter.component.html',
  styleUrls: ['./button-filter.component.scss']
})
export class ButtonFilterComponent implements OnInit {
  @Input() filters?: SelectOption<any>[];
  hover = false;

  constructor(public dialog: MatDialog, public overlay: Overlay) {}

  ngOnInit(): void {}

  openSidePanel(): void {
    this.dialog.open(SidePanelRadioFilterComponent, {
      panelClass: 'dialog-side-panel',
      width: '95vw',
      maxWidth: '100vw',
      height: '100%',
      data: this.filters ? this.filters : {} // send in what we know
    });
  }
}

@truttos
Copy link

truttos commented Aug 10, 2020

Can someone advise what is a good way to disable background page refresh animation while doing a retry on an open mat dialog component?

@icapri
Copy link

icapri commented Oct 5, 2020

Ok, the question is that when the open() method of the MatDialog is called, the Angular Material Component itself animates the opening of the dialog overlay. So, in my situation, I have created a typescript function file under shared called dialog-animator.ts which looks as follows:

/**
 * Animates an overlaid mat dialog on close.
 */
export function fadeOut() {
   const overlays = document.getElementsByClassName('my-overlay');
   for (let i = 0; i < overlays.length; i++) {
      overlays[overlays.length - 1].animate([
            { transform: 'scale(1, 1)' },
            { transform: 'scale(0, 0)' }
         ], { duration: 400, iterations: 1 });
   }
}

This function is imported and called when I click on x to close the mat dialog to the dialog.component.ts file.

Maybe this is one of the worst ways to solve the issue but I didn't want to install the ng-dialog-animation package.

@rajesh05c4
Copy link

In Angular latest version I was able to with keyframeAnimationOptions.
set the animation property something like this.

animation: { 
        to: 'aside',
        incomingOptions: {
          keyframeAnimationOptions: { duration: 300 }
        },
        outgoingOptions: {
          keyframeAnimationOptions: { duration: 300 }
        }
      },

@kaminkaa
Copy link

@rajesh05c4 where in the angular project did you use these keyframeAnimationOptions? In which class?

@spock123
Copy link

@omaracrystal

Your solution works, thanks.
However, the original animation still runs - any idea how to disable that, making it possible to also remove the animation delay?

@Plonq
Copy link

Plonq commented Dec 18, 2020

Our solution to this is to use NoopAnimationsModule, then implement custom animations using custom panel/backdrop classes, and global CSS. Similar to this solution. This also works for other components like MatMenu and MatDatePicker.

.custom-mat-dialog-panel {
  .mat-dialog-container {
    animation: dialog-animation;
  }
}

// If you want to customise the backdrop animation
.custom-mat-dialog-backdrop {
  transition: none;
  animation: backdrop-animation;
}

However this caused another problem - because no angular animations were playing, if a dialog was opened from MatMenu, then MatMenu would steal focus away from the dialog (focussing the trigger element). I believe this is because when using angular animations, the dialog doesn't capture focus until after the animations finish, which is also after MatMenu has set focus. We managed to work around this by telling the dialog to recapture focus:

const ref = this.dialog.open(...);
ref.afterOpened().subscribe(() => {
  setTimeout(() => {
    ref._containerInstance._recaptureFocus();
  });
});

If you abstract opening of dialogs into a service then this only needs to be done once. Another workaround is to call dialog.open() in setTimeout(), however that would need to be implemented everywhere that you open a dialog, and therefore isn't as great.

@mohitvirli
Copy link

Any updates on this or do we still need to use the workarounds mentioned? 😞

@crisbeto
Copy link
Member

We're in the process of cleaning up our issue backlog. Unfortunately I'll have to close this issue, because the Material dialog uses the @angular/animations package which doesn't have an API that would allow us to make the animations extensible. Since the issue was opened, we have a couple of workarounds that may work for you:

  1. If all you care is about the duration of the animation, you can try using the enterAnimationDuration or exitAnimationDuration options.
  2. In version 14 we introduced the new @angular/cdk/dialog package and based the Material dialog on top of it. The APIs for the two packages are very similar, but the CDK version doesn't come with any styling so you'll have much more control over the animation. There's an example of it on the docs site: https://material.angular.io/cdk/dialog/overview#cdk-dialog-styling

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jul 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: material/dialog feature This issue represents a new feature or feature request rather than a bug or bug fix needs: discussion Further discussion with the team is needed before proceeding P5 The team acknowledges the request but does not plan to address it, it remains open for discussion
Projects
None yet
Development

No branches or pull requests