Angular Animation For Dynamically Changing Height

后端 未结 4 1169
日久生厌
日久生厌 2020-12-14 02:56

I\'ve successfully gotten a panel to animate expanding and closing when entering and leaving the DOM. The problem is I now have a busy indicator inside the panel prior to sh

相关标签:
4条回答
  • 2020-12-14 03:31

    You can achieve something similar with a bit of css and js:

    Solution:

    component.ts

    import { Component, OnChanges, ViewChild, Input } from '@angular/core';
    
    @Component({
      selector: 'app-exandable',
      templateUrl: './exandable.component.html',
      styleUrls: ['./exandable.component.css']
    })
    export class ExandableComponent implements OnChanges {
    
      @Input()
      src;
    
      @ViewChild('expandable')
      expandable;
    
      ngOnChanges() {
        this.updateHeight();
      }
    
      updateHeight(delay = 0) {
        const el = this.expandable.nativeElement;
    
        setTimeout(() => {
    
          const prevHeight = el.style.height;
          el.style.height = 'auto';
          const newHeight = el.scrollHeight + 'px';
          el.style.height = prevHeight;
    
          setTimeout(() => {
            el.style.height = newHeight;
          }, 50);
        }, delay);
      }
    }
    

    css

    .expandable {
      transition: height 0.2s ease-in-out;
      overflow: auto;
    }
    

    Code:

    https://stackblitz.com/edit/angular-il71da

    0 讨论(0)
  • 2020-12-14 03:31

    Transitions and unfixed sizes (fit-content, max-content, etc..) do not communicate well.

    Here's a sample of hack for this case:

    animations: [
        trigger('openCloseAnimation', [
          state('open', style({ maxHeight: '100px', overflow: 'auto' })),
          state('closed', style({ maxHeight: '60px' })),
          transition('* => closed', animate('0.2s')),
          transition('* => open', animate('0.5s')),
        ]),
      ],
    

    With MaxHeight, your div/container dont will exceed more than 'max-content', but will behave with 'fit-content'.

    0 讨论(0)
  • 2020-12-14 03:51

    I've written a component that smoothly animates the height of projected content if that content changes. It's used like this:

    <smooth-height [trigger]="content">
      {{content}}
    </smooth-height>
    

    Here's a stackblitz: https://stackblitz.com/edit/angular4-kugxw7

    This is the component:

    import {ElementRef, HostBinding, Component, Input, OnChanges} from '@angular/core';
    import {animate, style, transition, trigger} from "@angular/animations";
    
    @Component({
      selector: 'smooth-height',
      template: `
        <ng-content></ng-content>
      `,
      styles: [`
        :host {
          display: block;
          overflow: hidden;
        }
      `],
      animations: [
        trigger('grow', [
          transition('void <=> *', []),
          transition('* <=> *', [
            style({height: '{{startHeight}}px', opacity: 0}),
            animate('.5s ease'),
          ], {params: {startHeight: 0}})
        ])
      ]
    })
    export class SmoothHeightComponent implements OnChanges {
      @Input()
      trigger: any;
    
      startHeight: number;
    
      constructor(private element: ElementRef) {}  
    
      @HostBinding('@grow') get grow() {
        return {value: this.trigger, params: {startHeight: this.startHeight}};
      }
    
      setStartHeight(){
        this.startHeight = this.element.nativeElement.clientHeight;
      }
    
      ngOnChanges(){
        this.setStartHeight();
      }
    }
    
    0 讨论(0)
  • 2020-12-14 03:54

    I made a directive based on @MartinCremer answer. I think using a directive makes more sense since by doing that, you also should add the animation to your parent component (and it's close the standard way of adding animations).

    So inside my animations.ts file. I've added the animation:

    export const smoothHeight = trigger('grow', [
      transition('void <=> *', []),
      transition('* <=> *', [style({ height: '{{startHeight}}px', opacity: 0 }), animate('.5s ease')], {
        params: { startHeight: 0 }
      })
    ]);
    

    then you should add this animation to your parent component (the component that you want to use the animation inside it):

    import { smoothHeight } from '@app/animations';
    @Component({
      selector: 'app-parent',
      templateUrl: './parent.component.html',
      styleUrls: ['./parent.component.scss'],
      animations: [smoothHeight]
    })
    

    And here is the directive which is really close to the component of @MartinCremer:

    import { Directive, OnChanges, Input, HostBinding, ElementRef } from '@angular/core';
    
    @Directive({
      selector: '[smoothHeight]',
      host: { '[style.display]': '"block"', '[style.overflow]': '"hidden"' }
    })
    export class SmoothHeightAnimDirective implements OnChanges {
      @Input()
      smoothHeight;
      pulse: boolean;
      startHeight: number;
    
      constructor(private element: ElementRef) {}
    
      @HostBinding('@grow')
      get grow() {
        return { value: this.pulse, params: { startHeight: this.startHeight } };
      }
    
      setStartHeight() {
        this.startHeight = this.element.nativeElement.clientHeight;
      }
    
      ngOnChanges(changes) {
        this.setStartHeight();
        this.pulse = !this.pulse;
      }
    }
    

    Finally inside parent.component.html use the directive:

    <div [smoothHeight]="yourAnimationIndicator">
      // any html content goes here
    </div>
    

    Just replace yourAnimationIndicator with the variable that the animation should trigger on change of its value.

    0 讨论(0)
提交回复
热议问题