16

I have created a Layout Manager in Angular which can take in Components and then Display it in View and add animations to it while each component is shown in View and Goes out of view .

At a single instance of time either one panel or max of two panels can be shown in view .

This is the Stackblitz link to the Same the problem with this is the transitions are not smooth and also it does appear as streamlined as it should be the design is as follows .

Design

Now what i am trying to achieve is when the app is loaded 1-2 are shown by default but as i change the panels the transitions change like for eg

1-3 as 2 is moving out of view it should slide left and easeout and 3 should should in and ease out. and then if from 1-3 we go to 2-3 1 should ease out to the right and 2 should slide in .

Also the panels can take some percentages(33 %, 66% or 100% ) of the screen width .

I am not sure if i am able to explain it properly I have been stuck on this for weeks with transitions if anyone can help it will be awesome, Thanks

Thanks to Saddam who helped create this Animation this is exactly what i want from animations - https://i.stack.imgur.com/UXsg2.jpg this is for visual purpose only

Rahul Singh
  • 19,030
  • 11
  • 64
  • 86
  • Please explain more about your issue, for ex 1-2 is panel1 and panel2 what should happen to them when page is loaded, how the transition would be. – Just code Dec 27 '18 at 05:32
  • When the page loads we can have a transition from left to right or simply have no transitions when the page loads first time after then the transition starts depending upon which panel is in and out of view – Rahul Singh Dec 27 '18 at 05:36
  • Have you tried angular animations? – Sergey Dec 31 '18 at 12:10
  • 3
    Unfortunately I haven't managed to understand what exactly you want to achieve except of some kind of animation. However, you could try to learn Angular Animations it should help https://blog.angularindepth.com/total-guide-to-dynamic-angular-animations-that-can-be-toggled-at-runtime-be5bb6778a0a . Also if you made a some kind of animations (in any graphical editor) there is a chance that we would better understand your desire. – Sergey Dec 31 '18 at 12:17
  • i have looked into animations in a lot of detail and gone through it several times and i agree with you a graphical animation will be really help ful i will try and see it this is possible as i am not versed in any graphical editors – Rahul Singh Jan 02 '19 at 06:45
  • Personally i would animate whole container instead of single child components. – Antoniossss Jan 02 '19 at 08:21
  • can you give any insites on how to do the same i am open for suggestions but as individual components can load and unload depending upon the view i am not sure – Rahul Singh Jan 02 '19 at 10:57
  • To help other people understand what you want to achieve, I have created a simple animation. [https://imgur.com/a/qZ3vtDb ] @RahulSingh Is this what you want to achieve with your layout? (It contains [1-3] and [2-3] animation) – Saddam Pojee Jan 02 '19 at 13:27
  • @SaddamPojee Yes that exactly what i need with the panes thanks a ton – Rahul Singh Jan 03 '19 at 09:34
  • any idea on it @SaddamPojee – Rahul Singh Jan 04 '19 at 09:22
  • RahulSingh, At the moment, I don't know how to solve your problem. But I think, the link which @Sergey provided and these links will help you in solving your problem. [1] https://medium.com/frontend-coach/angular-in-motion-4-approaches-to-animation-1aa7426aae5a [2] https://medium.com/@asm/animated-slide-panel-with-angular-e985ad646f9 [3] https://angular.io/guide/animations [4] https://github.com/antonmoiseev/demo-slide-panel – Saddam Pojee Jan 04 '19 at 12:43
  • Have a look in how it's done in this Swiper: https://www.npmjs.com/package/angular2-useful-swiper . While configuring SwiperOptions there's onReachEnd and onProgress. This functionality is similar / allows to build something similar to what you try to achieve. – Tiago Martins Peres Jan 09 '19 at 08:45
  • need reputation help me :) – Atiq Ur Rehman Jan 11 '19 at 06:10

1 Answers1

7

I have changed the PanelComponent in your StackBlitz sample to work in the way the provided animation is showing.

You need just three states. One when the component is initially outside on the right. From there it moves into view this is the second state. After that it moves out of view to the left the third state. Once it is out of view to the left you move it back to the right initial state so it can come back in when needed.

Here is the code for the changed panel component:

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

@Component({
  selector: 'my-panel',
  templateUrl: './panel.component.html',
  styleUrls:['./panel.component.css'],
  animations: [
    trigger('transition', [
      state('right', style({
        transform: 'translateX(100%)',
        opacity: 0
      })),
      state('inview', style({
      })),
      state('left', style({
        transform: 'translateX(-100%)',
        opacity: 0
      })),
      transition('right => inview', [
        animate(`${PanelComponent.ANIMATION_DURATION}ms 0ms ease-out`,style({ 
          transform: 'translateX(0)',
          opacity: 1 }))
      ]),
      transition('inview => left', [
        animate(`${PanelComponent.ANIMATION_DURATION}ms 0ms ease-in`,style({ 
          transform: 'translateX(-100%)',
          opacity: 0 }))
      ])
    ])]
})
export class PanelComponent  {
  public static readonly ANIMATION_DURATION = 500;
  @Input() destroyOnHidden: boolean = false;
  @Input() id : string = null;
  @ContentChild('layout') contentChild : QueryList<ElementRef>;
  @HostBinding('style.width') componentWidth = null;
  @HostBinding('style.height') componentHeight = null;
  @HostBinding('style.overflow') componentOverflow = null;
  public state: string = null;

  public getId() {
    return this.id;
  }

  constructor() {
    this.state = 'right';
  }

  public setInViewStyle(width: string, height: string, overflow: string): void {
    this.componentWidth = width + '%';
    this.componentHeight = height + '%';
    this.componentOverflow = overflow;
    this.state = 'inview';
  }

  public setDefault(): void {
    this.state = 'right';
  }

  public moveOut(): void {
    this.state = 'left';
  }


  public transitionDoneHide(): void {
    if(this.state === 'right') {
      console.log('hiding transition done');
      this.componentWidth = '0' + '%';
      this.componentHeight = '0' + '%';
      this.componentOverflow = 'hidden';
    }
  }
}

As you see I have split the setStyle into two methods setInViewStyle and moveOut. The setInViewStyle sets the panel style and moves it into view. The moveOut method moves the panel out of view to the left. Because of this I have also changed the layout manager method panelTransformation.

Here is the changed code:

panelTransformation(transitions) {
    if (transitions) {
      let movement = null;
      let panelsToRemove = [];
      let panelsToAdd = [];
      if (this.previousPanels) {
        panelsToRemove = this.previousPanels.filter((panel) => transitions.panel.indexOf(panel) < 0);
        panelsToAdd = transitions.panel.filter((panel) => this.previousPanels.indexOf(panel) < 0);
      } else {
        panelsToAdd = transitions.panel
      }

      if (panelsToRemove.length > 0) {
        for (let panelToRemove of panelsToRemove) {
          this.idPanelMap.get(panelToRemove).moveOut();
        }
        // wait for fade out to finish then start fade in
        timer(PanelComponent.ANIMATION_DURATION + 100).subscribe(() => {
          for (let panelToAdd of panelsToAdd) {
            this.idPanelMap.get(panelToAdd).setInViewStyle(transitions.width[transitions.panel.indexOf(panelToAdd)], '100', 'initial');
          }
          for (let panelToRemove of panelsToRemove) {
            this.idPanelMap.get(panelToRemove).setDefault();
          }
        });
      } else { // first time so just fade in
        for (let panelToAdd of panelsToAdd) {
          this.idPanelMap.get(panelToAdd).setInViewStyle(transitions.width[transitions.panel.indexOf(panelToAdd)], '100', 'initial');
        }
      }

      this.previousPanels = transitions.panel;
    }
  }

As you can see I have completely changed the logic so I first move out panels that have to be removed wait for the animation to finish and then move in the new panels. Here is a link to my StackBlitz sample that implements all this so you can also see it working.

As requested in the comment I provide also another sample with moving in both directions. This makes things more complicated. I had to add one more transition for the move in the other direction. And added the possibility to set the direction in the moveOut method. In my opinion it does not look so good because it can have some flicker. The new panel component code:

import { Component, ContentChild, QueryList,HostBinding,Input,ElementRef } from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition
} from '@angular/animations';
import { timer } from 'rxjs';

@Component({
  selector: 'my-panel',
  templateUrl: './panel.component.html',
  styleUrls:['./panel.component.css'],
  animations: [
    trigger('transition', [
      state('right', style({
        transform: 'translateX(100%)',
        opacity: 0
      })),
      state('inview', style({
      })),
      state('left', style({
        transform: 'translateX(-100%)',
        opacity: 0
      })),
      transition('right => inview', [
        animate(`${PanelComponent.ANIMATION_DURATION}ms 0ms ease-out`,style({ 
          transform: 'translateX(0)',
          opacity: 1 }))
      ]),
      transition('inview => left', [
        animate(`${PanelComponent.ANIMATION_DURATION}ms 0ms ease-in`,style({ 
          transform: 'translateX(-100%)',
          opacity: 0 }))
      ]),
      transition('inview => right', [
        animate(`${PanelComponent.ANIMATION_DURATION}ms 0ms ease-in`,style(         { 
          transform: 'translateX(100%)',
          opacity: 0
       }))
      ]),
      transition('left => inview', [
        animate(`${PanelComponent.ANIMATION_DURATION}ms 0ms ease-out`,style({ 
          transform: 'translateX(0)',
          opacity: 1 }))
      ])
    ])]
})
export class PanelComponent  {
  public static readonly ANIMATION_DURATION = 500;
  public static readonly ANIMATION_DELAY = 100;
  @Input() destroyOnHidden: boolean = false;
  @Input() id : string = null;
  @ContentChild('layout') contentChild : QueryList<ElementRef>;
  @HostBinding('style.width') componentWidth = null;
  @HostBinding('style.height') componentHeight = null;
  @HostBinding('style.overflow') componentOverflow = null;
  public state: string = null;
  private lastDirection: 'left' | 'right';

  public getId() {
    return this.id;
  }

  constructor() {
    this.state = 'right';
  }

  public setInViewStyle(width: string, height: string, overflow: string): void {
    this.componentWidth = width + '%';
    this.componentHeight = height + '%';
    this.componentOverflow = overflow;
    this.state = 'inview';
  }

  public setDefault(): void {
    this.state = 'right';
  }

  public moveOut(direction: 'left' | 'right'): void {
    this.lastDirection = direction;
    this.state = direction;
  }


  public transitionDoneHide(): void {
    if(this.state === this.lastDirection) {
      if (this.lastDirection === 'right') {
        timer(PanelComponent.ANIMATION_DELAY).subscribe(() => this.hide());
      } else {
        this.hide();
      }
      console.log('hiding transition done');

    }
  }

  private hide() {
    this.componentWidth = '0' + '%';
    this.componentHeight = '0' + '%';
    this.componentOverflow = 'hidden';
  }
}

In the panelTransformation method I added the logic to set the direction for moving out to right if it is the first panel. This is the updated code:

panelTransformation(transitions) {
  if (transitions) {
    let movement = null;
    let panelsToRemove = [];
    let panelsToAdd = [];
    if (this.previousPanels) {
      panelsToRemove = this.previousPanels.filter((panel) => transitions.panel.indexOf(panel) < 0);
      panelsToAdd = transitions.panel.filter((panel) => this.previousPanels.indexOf(panel) < 0);
    } else {
      panelsToAdd = transitions.panel
    }

    if (panelsToRemove.length > 0) {
      for (let panelToRemove of panelsToRemove) {
        let direction: 'left' | 'right' = 'left';
        // if it is the first panel we move out right
        if (this.previousPanels.indexOf(panelToRemove) === 0) {
          direction = 'right';
        }
        this.idPanelMap.get(panelToRemove).moveOut(direction);
      }
      // wait for fade out to finish then start fade in
      timer(PanelComponent.ANIMATION_DURATION + PanelComponent.ANIMATION_DELAY).subscribe(() => {
        for (let panelToAdd of panelsToAdd) {
          this.idPanelMap.get(panelToAdd).setInViewStyle(transitions.width[transitions.panel.indexOf(panelToAdd)], '100', 'initial');
        }
        for (let panelToRemove of panelsToRemove) {
          this.idPanelMap.get(panelToRemove).setDefault();
        }
      });
    } else { // first time so just fade in
      for (let panelToAdd of panelsToAdd) {
        this.idPanelMap.get(panelToAdd).setInViewStyle(transitions.width[transitions.panel.indexOf(panelToAdd)], '100', 'initial');
      }
    }

    this.previousPanels = transitions.panel;
  }
}

And also the StackBlitz sample for this implementation.

Aleš Doganoc
  • 11,568
  • 24
  • 40
  • really thank you i guess you have nailed it but one slight thing i would ask on this . in the example when we given panel 1 and 2 and then go to panel 2 and 3 it works great but from that view when we move to panel 1 and 2 again panel 1 should come from left don't you thing it is coming from right . any pointers how we control the direction as the order of panel is fixed for us 1 -> 2 -> -> 3- >4 – Rahul Singh Jan 11 '19 at 07:44
  • Please let me know if we can give the direction of the transition here , it would look great right as the panels are in order. even if you are not able to give anything on this i will give it to you thanks a ton. looking forward for your reply – Rahul Singh Jan 11 '19 at 07:45
  • I have added another implementation to the answer where you can specify the direction of the animation. – Aleš Doganoc Jan 11 '19 at 23:13
  • AlesD i get it there is some flicker i will try and refine the same thanks a ton, even 500 bounty was less for your answer i got some real insights on how we can achieve this thanks – Rahul Singh Jan 14 '19 at 05:41