Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to animate an element in Angular2 when it's model (text) changes

Tags:

angular

ionic2

I am trying to animate a value whenever the text changes in my Ionic 2 (Angular 2) project. This is in an Ionic2 project, but should be the same for any Angular2.

I have the following in the markup..

<ion-list no-lines>
    <ion-item *ngFor="let item of data>
      <ion-card [@flyInOut]='cardState'>
            <ion-card-content>
              <div ><b>{{item.name.description}}</b></div>
              <div [@valueUpdated]='item.lastname.description'>{{item.lastname.description}}</div>
              <div>{{item.value}}</div>
            </ion-card-content>
          </ion-card>

        </ion-item>
 </ion-list>

and the following trigger in the component..

 trigger('valueUpdated',[
        state('void => *',style({
           backgroundColor:'red'
       })),
       state('* => void',style({              
           backgroundColor:'green'
       })),
       transition('active => inactive', animate('100ms ease-in')),
       transition('inactive => active', animate('100ms ease-out'))
   ])

I am just using colors here to test, eventually I'd want it to change change color (and then change back), or perhaps have the old value "flyout" and the new to "flyin" (similar as the example in the Angular documentation.

However, before that, I just want to see if this is possible. I am new to the playing with the animations, so perhaps I am right off track?

Is it possible to do this, and if so how?

Any help greatly appreciated!

[UPDATE]

I have come close with the following, using translateX fot testing rather than color changes (as it is easier to see) ...

trigger('valueUpdated', [   
  state('* => *', style({ transform: 'translateX(0)' })),       
  transition('* => *', [      
    animate(2000, style({ transform: 'translateX(100%)'  }))
  ])
]),

When the element is first added I see the above animated translateX occurring, however went the state, ie item.name.description is changed, the div shoots straight to translateX(100%), and then back to translateX(0) with no animation to the translateX(100%), so still not quite right.

[UPDATE2]

I have found something comes closer..

 trigger('valueUpdated', [   
    state('void => *', style({ transform: 'translateX(0)' })),       
    transition('void => *', []),
     transition('* => *', [      
       animate(2000, style({ transform: 'translateX(100%)'  }))
    ])
   ]),

Adding the void => * state and transition stopped the animation occurring when the element is first added. It is now only animates when the text is first changed from it's initial value, but not any subsequent times, so still not right.

like image 826
peterc Avatar asked Jan 27 '17 06:01

peterc


2 Answers

Here's what I do. The animation occurs when the value is changed, every time, but not when it's created. You can add as many style items as you like. The offset is like a percentage of the animation duration. In the example below, the background and text color is immediately changed to yellow and red, and then slowly fades back to what is was before.

    trigger(
    'valueChanged',
    [
        transition('void => *', []),   // when the item is created
        transition('* => void', []),   // when the item is removed
        transition('* => *', [         // when the item is changed
            animate(1200, keyframes([  // animate for 1200 ms
                style ({ background : '#FFD900', color: '#FF5500', offset: 0.0 }),
                style ({ background : 'inherit', color: 'inherit', offset: 1.0 }),
            ])),
        ]),
    ]),

HTML code:

        <div [@valueChanged]="onCount">
            {{onCount}}
        </div>
like image 135
leppen Avatar answered Oct 13 '22 10:10

leppen


I solved the problem: the trick is to use the variable with content as an animation state, instead of separate variable. So, when the content is changed, Angular sees it as a modification of animation state and it kicks in the playback of * => * rule.

I created a single-file component to help everyone check this out:

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

export const fadeInOutTimeout = 250;
export const fadeInOut = trigger('fadeInOut', [
  transition('void => *', [style({ opacity: '0', transform: 'translateX(-10%)' }), animate(fadeInOutTimeout)]),
  transition('* => void', [animate(fadeInOutTimeout, style({ opacity: '0' }))]),
  transition('* => *', [
style({ opacity: '0', transform: 'translateX(-10%)' }),
animate(fadeInOutTimeout, style({ opacity: '1', transform: 'translateX(0%)' })),
  ]),
]);

@Component({
  selector: 'app-animated-text',
  template: `
    <div [@fadeInOut]="text" *ngIf="text != null">
      {{ text }}
    </div>
  `,
  styles: [],
  animations: [fadeInOut],
})
export class AnimatedTextComponent {
  @Input() text: string;
}

please notice <div [@fadeInOut]="text" ... - this is where we use the text itself as an animation state.

To use this component simply add the following to the parent component:

  <app-animated-text [text]="myText"></app-animated-text>

So whenever myText variable changes, the component will animate the transition by hiding old text and fading & flying the new text in.

like image 30
VeganHunter Avatar answered Oct 13 '22 11:10

VeganHunter