Home > Mobile >  angular: pass data to animation
angular: pass data to animation

Time:05-26

I've created a bootstrap modal component for angular, but I'd like to choose from which direction the modal enters the window. I've Bootstrap modal for angular

At the moment my EnterLeaveAnimation looks like this:

export const EnterLeaveAnimation =
  trigger('enterLeave', [
    transition(
      ':enter', [
        style({ top: '-50%' }), // <-- I should be able to change this dynamically, eg: left: '-50%'
        animate('{{ duration }} ease-in-out', style({ top: 0 })),
      ], {
        params: {
          duration: '500ms'
        }
      }
    ),
    transition(
      ':leave', [
        animate('{{ duration }} ease-in-out', style({ top: '-50%' }))
      ], {
        params: {
          duration: '500ms'
        }
      }
    ),
  ]);

ATM this animation is applied to the div like this:

<div  [@fadeInOut] [@enterLeave] *ngIf="isOpen">
  <!-- [fromDirection]="fromDirection" -->
  ...
</div>

<div [@fadeInOut] *ngIf="isOpen">
  <div  [class.show]="isOpen"></div>
</div>

But would eventually end up looking like this:

<div  [@fadeInOut] [@enterLeave]="{ value: isOpen ? ':enter' : ':leave', params: { fromDirection: fromDirection } }" *ngIf="isOpen">
  ...
</div>

<div [@fadeInOut] *ngIf="isOpen">
  <div  [class.show]="isOpen"></div>
</div>

The problem I have now, is that I can't figure out how I can modify my EnterLeaveAnimation to switch on the fromDirection and apply different style rules accordingly:

style({ top: '-50%' })
OR
style({ bottom: '-50%' })
OR
style({ left: '-50%' })
OR
style({ right: '-50%' })

As you can see, I'm already passing my duration parameter on to the animation, but how do I switch on a parameter and use the style function accordingly?

CodePudding user response:

I'am afraid that you should make the animation "manually"

Don't worry, it's easy, see this SO for a simple e.g.

With this idea we can defined in your host component some like

  animate(element: any, state: string, background: boolean) {
    if (element) {
      let customstyle =
        state == 'enter'
          ? background
            ? { opacity: 0 }
            : { top: '-50%', left: '0', opacity: 0 }
          : background
          ? { opacity: 1 }
          : { top: '-100%', left: '0', opacity: 1 };
      if (!background) {
        switch (this.fromDirection) {
          case 'bottom':
            customstyle =
              state == 'enter'
                ? { top: '50%', left: '0', opacity: 0 }
                : { top: '100%', left: '0', opacity: 0 };
            break;
          case 'left':
            customstyle =
              state == 'enter'
                ? { top: '0', left: '-100%', opacity: 0 }
                : { top: '0', left: '-100%', opacity: 0 };
            break;

          case 'right':
            customstyle =
              state == 'enter'
                ? { top: '0', left: '100%', opacity: 0 }
                : { top: '0', left: '100%', opacity: 0 };
            break;
        }
      }
      const myAnimation =
        state == 'enter'
          ? this.builder.build([
              style(customstyle),
              animate(this.timing, style({ top: 0, left: 0, opacity: 1 })),
            ])
          : this.builder.build([animate(this.timing, style(customstyle))]);
      this.player = myAnimation.create(element);
      if (state == 'leave') {
        this.player.onDone(() => {
          this.componentInstance.instance.isOpen = false;
        });
      }
      this.player.play();
    }
  }

You also define in your host component a function close

  close() {
    this.isOpen = false;
    this.animate(
      (this.componentInstance.instance as any).modal.nativeElement,
      'leave',
      false
    );
    this.animate(
      (this.componentInstance.instance as any).modalBackground.nativeElement,
      'leave',
      true
    );
  }

And use the getter to call to animate

@Input() set isOpen(value: boolean) {
    this._isOpen = value;
    if (this.componentInstance) {
      if (value) {
        this.componentInstance.instance.isOpen = value;
        this.animate(
          (this.componentInstance.instance as any).modal.nativeElement,
          'enter',
          false
        );
        this.animate(
          (this.componentInstance.instance as any).modalBackground
            .nativeElement,
          'enter',
          true
        );
      } else {
        this.animate(
          (this.componentInstance.instance as any).template.nativeElement,
          'leave',
          false
        );
        this.animate(
          (this.componentInstance.instance as any).modalBackground
            .nativeElement,
          'leave',
          true
        );
      }
    }
    this.isOpenChange.emit(value);
  }

As you see the animate "reemplace" your animations, but we can not use *ngIf to show/hide the modal else [ngClass] in the way

<!---modal.component.html-->
<div #modal [ngClass]="isOpen?'modal d-block':'modal d-none'" class='modal d-block'>
  <div >
    <div >
      <ng-container *ngTemplateOutlet="template"></ng-container>
    </div>
  </div>
</div>
<div #modalBackground [ngClass]="isOpen?'d-block':'d-none'">
  <div  [class.show]="isOpen"></div>
</div>

See how you use template reference variable to get the background and the content, so we need use ViewChild

  @ViewChild('modal') modal:ElementRef
  @ViewChild('modalBackground') modalBackground:ElementRef

NOTE: In the stackblitz I "hardcode the "timing", you can use an @Input or another way.

  • Related