angular / angular

Deliver web apps with confidence 🚀
https://angular.dev
MIT License
95.92k stars 25.35k forks source link

[Animations, Angular 5] inner element is not animated #20823

Open tim-kuteev opened 6 years ago

tim-kuteev commented 6 years ago

I'm submitting a...


[x] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

When the animation is used for the parent element, the animation for the child element does not work.

Expected behavior

I expect that the animation for the inner element will work at the same time with the animation of the parent, since it worked before.

Minimal reproduction of the problem with instructions

http://embed.plnkr.co/9JUga9LYw0NEDmdSempP/ it works as expected if set angular version ^4.2.0 (Also works as expected with the version set to 5.0.2, but breaks in 5.0.3. thx jamesthurley)

What is the motivation / use case for changing the behavior?

To animate nested elements.

Environment


Angular version: 5.0.5


Browser:
- [x] Chrome (desktop) version 62

For Tooling issues:
- Node version: 8.9.1  
- Platform:  Win 10 

Others:

jamesthurley commented 6 years ago

Also works as expected with the version set to 5.0.2, but breaks in 5.0.3.

mattiLeBlanc commented 6 years ago

I might have something similar, related: https://github.com/angular/angular/issues/20811

I have a nested animation that should be delayed, but is executed at the same time as the parent animation. This worked fine in A 4.4.2 but fails in 5.0.5

samthe13th commented 6 years ago

I'm having the same problem

tim-kuteev commented 6 years ago

Is there any chance that someone will be assigned to this one in the near future?

ghost commented 6 years ago

The same problem, any solution to this??

Pedro-vk commented 6 years ago

As @jamesthurley sais, I've fixed the version to 5.0.2 and it works fine.

ghost commented 6 years ago

Thank you @Pedro-vk

MrWolfZ commented 6 years ago

Just in case anyone stumbles on this issue again: If you want both animations to play at the same time you simply need to use group and animateChild. Basically, in the plunker linked in the first post you have to replace outerTransition with the following:

const outerTransition = transition('void => *', [
  style({opacity: 0}),
  group([
    animate(2000, style({opacity: 1})),
    query('@inner', [
      animateChild()
    ]),
  ]),
]);
silvanaweb commented 6 years ago

I am not sure if you @tim-kuteev are talking about the same issue i had, therefore i would like to provide my case and I would like to ask to @MrWolfZ if his change is applying to my case as well.

I was using one flag variable (true/false) and using it for 2 triggers, one used on the parent DIV and one in the child (children) DIVS. So the trigger applied to the parent was working, but the one applied to the children was not working. Example:

<div @animateParent *ngIf="showButtons">
        <div @animateChild *ngIf="showButtons">...</div>
</div>
MrWolfZ commented 6 years ago

@silvanaweb I don't see a reason why it shouldn't apply. Just do this:

const parentAnimation = trigger('animateParent', [
  transition('void => *', [
    style({opacity: 0}),
    group([
      animate(2000, style({opacity: 1})),
      query('@animateChild', [
        animateChild()
      ]),
    ]),
  ]);
])
Pedro-vk commented 6 years ago

I can't solve it with the animateChild() because my child animation has different state.

import { trigger, state, style, transition, animate, keyframes, AnimationEntryMetadata } from '@angular/core';

const defaultBoxShadow = {boxShadow: `0 0 0 transparent`};

export const parentAnimation: AnimationEntryMetadata = trigger('parentAnimation', [
  state('*', style({height: '0'})),
  state('visible',   style({height: '*'})),

  transition('* => visible', animate('.45s ease')),
  transition('* => *', animate('.3s ease')),
]);

export const childAnimation: AnimationEntryMetadata = trigger('childAnimation', [
  state('hidden', style({transform: '*'})),
  state('move', style({transform: '*'})),
  state('visible',   style({transform: 'translateY(0%)'})),

  transition('hidden => move', animate('.4s', keyframes([
    style({offset: 0, ...defaultBoxShadow}),
    style({offset: 0.5, boxShadow: '0 0 30px blue'}),
    style({offset: 1, ...defaultBoxShadow}),
  ]))),
  transition('* => visible', animate('.45s ease')),
  transition('* => *', animate('.3s ease')),
]);
tim-kuteev commented 6 years ago

@silvanaweb @Pedro-vk here is solution which worked for me: https://embed.plnkr.co/vXhLXOMWYzWkJ8DgMvrI/

I've added trigger to the parent of parent element:

trigger('nested', [
  transition('* => *', [
    query('@*', animateChild(), {optional: true})
  ])
]),

It's selecting all of the nested animations and applying animateChild behavior.

silvanaweb commented 6 years ago

Hi, I have tried all the suggested solutions, but none works, the child does not animate :(

MrWolfZ commented 6 years ago

@silvanaweb Can you share your code? Maybe we'll be able to help.

olaini-lature commented 6 years ago

@MrWolfZ I have the same problem.

transisi.component.ts

@Component({
  selector: 'app-transisi',
  templateUrl: './transisi.component.html',
  styleUrls: ['./transisi.component.css'],
  animations:[
        trigger('changeState',[
            state('first', style({
                opacity: '1',
                backgroundImage: 'url(./../../assets/img/fixle2.jpg)',// harusnya ada logo dan tombol, CSS to HTML?
            })),
            state('second', style({
                opacity: '1',
                backgroundImage: 'url(./../../assets/img/fixle1.jpg)',
            })),
        state('third', style({
          opacity: '1',
        })),
        state('fourth', style({
          opacity: '0',
        })),
        transition('first <=> second',animate('3s ease'))

            ]),
      trigger('anotherState',[
        state('pertama', style({
            opacity: '1'
        })),
        state('kedua', style({
            opacity: '0'
        })),
        transition('pertama <=> kedua',animate('3s ease'))
        ]),
  ]
})

I want trigger changeState and anotherState to be animate in same time. Help me.

silvanaweb commented 6 years ago

@MrWolfZ I have learned new things about query, so i will try it again during the weekend and eventually post my code if i still fail to make it work.

olaini-lature commented 6 years ago

@silvanaweb could u help me to solve my questions?

MrWolfZ commented 6 years ago

@olanRay94 Could you please also share your HTML template?

silvanaweb commented 6 years ago

I finally managed to solve my problem :) So the point is that I did not know about the query feature that allows to render children element using the same state. I show my example. This is the HTML code:

 <div class="buttons-container" *ngIf="slideButtons" @buttonsState>
      <div class="icons-top">
        <button (click)="openIdeate()">
          <img class=" idea-icon" src="assets/imgs/idea.svg" >
        </button>
</div>

So buttonState is the state that animate the parent and then i want to animate the child img.idea-icon as well. So. in the animation I write:

 animations: [
    trigger('buttonsState', [
      transition(':enter', [
        group([
          query(':self', [
            style({height: 0}),
            animate(200, style({height: 200}))
          ]),
          query('.idea-icon', [
            style({
              transform: 'scale(0)',
              transformOrigin: 'center'
            }),
            animate(500, style({
              transform: 'scale(1)',
              transformOrigin: 'center'
            }))
          ]) ])    ])  ]),

Where :self is the parent, .idea-icon is my image that i want to scale in size. One important note is that w/h of the image should be set in the CSS.

silvanaweb commented 6 years ago

@olanRay94 it is weird that they are not triggered at the same time. But if they are like mine, in the same block, then you could use query in the way I have done. The function group allows you to make them start at the same time and not sequentially. Another note is that in some cases you must add {optional: true} as last parameter of query, in the case when the animation could be triggered or not (anyway, Angular will complain in that case, so you do not need to wonder which is the case).

jpduckwo commented 6 years ago

Definitely check out animateChild()

Here's an example where two animations happen in sequence. Notice that the order in the transition array determines if the child animation gets fired first or after.

In this case when going from inactive > active the parent container animates first. But when going back to inactive the reverse happens and the child inmates then the parent.

The elements in the transition array happen sequentially - so that the the 2nd one waits for the first to finish before animating. As @MrWolfZ says putting them in a group will do them sequentially.

...
    trigger('containerState', [
      state('inactive', style({ height: '0' })),
      state('active', style({ height: '*' })),
      transition('inactive => active', [
        animate('150ms ease-in-out'),
        query('@innerState', [
          animateChild(),
        ])
      ]),
      transition('active => inactive', [
        query('@innerState', [
          animateChild(),
        ]),
        animate('150ms ease-in-out'),
      ])
    ]),
    trigger('innerState', [
      state('inactive', style({ opacity: 0, transform: 'translateY(-100%)' })),
      state('active', style({ opacity: 1, transform: 'translateY(0)' })),
      transition('inactive => active', [
        animate('150ms ease-in-out'),
      ]),
      transition('active => inactive', [
        animate('150ms ease-in-out'),
      ])
    ]),
...
<div class="container"
  [@containerState]="state">
  <div class="innerContainer" [@innerState]="state">
    Blah
  </div>
</div>
aproberts01 commented 6 years ago

I've tried all of the suggested solutions - still no luck. Can anyone help me out?

import {
  trigger,
  style,
  transition,
  animate,
  query,
  group,
  animateChild
} from "@angular/animations";

const expansionAnimation = transition("false <=> true", [
  group([
    query(
      "@expansionTrigger",
      [
        style({
          height: 0
        }),
        animate(2000, style({ height: "*" }))
      ],
      { optional: true }
    ),
    query(".col-animation", [
      style({
        "-webkit-box-flex": "0",
        flex: "0 0 66.66667%",
        "max-width": "66.66667%"
      }),
      animate(
        2000,
        style({
          "flex-basis": "0",
          "-webkit-box-flex": "1",
          "flex-grow": "1",
          "max-width": "100%"
        })
      )
    ])
  ])
]);

export const Animations = {
  animations: [
    trigger("expansionTrigger", [expansionAnimation])
  ]
};
<div [@expansionTrigger]="isExpanded === 'true' ? 'true' : 'false'">
    <div class="col-animation">
    </div>
</div>
webcat12345 commented 6 years ago

I am having same issue

ghost commented 6 years ago

@aproberts01 Look at my StackOverflow answer maybe is gonna help you. https://stackoverflow.com/questions/48479524/how-to-impliment-a-sidenav-navigation-drawer-with-a-mini-variant/49884854#49884854

ghost commented 6 years ago

the trick is to use timeout some delay

DavyDeDurpel commented 6 years ago

I noticed that when nesting components with animations, the names of the states have to be unique. Otherwise transitions might be executed even when these are disabled.

So you can't have state 'active' in the parent and at the same time state 'active' in any of the children. Renaming the state of the child to something else fixed the problem for me.

I'm not sure if this is intended behaviour or a bug in Angular.

vovikdrg commented 6 years ago

Same issue with Angular Material Dialog. My requirements are to show component inside of modal and then i will dynamically replace components, so on :enter, :leave i need to do animation. Even callback is called but animation is blocked by parent.

https://github.com/angular/material2/blob/master/src/lib/dialog/dialog-animations.ts#L18

flash-me commented 3 years ago

I noticed that when nesting components with animations, the names of the states have to be unique. Otherwise transitions might be executed even when these are disabled.

So you can't have state 'active' in the parent and at the same time state 'active' in any of the children. Renaming the state of the child to something else fixed the problem for me.

I'm not sure if this is intended behaviour or a bug in Angular.

Angular 11.x here. This is still the case cheers flash ⚡

dario-piotrowicz commented 2 years ago

For what is worth I also wanted to mention (as other comments have too) that I think this does seem to be working as expected

As stated in the animateChild api docs parent's animations block their children's animations and animateChild's whole purpose is to address that

I've recreated the example in stackblitz and added a simple group, query and animateChild all I believe does work as it should: stackblitz