web-dev-qa-db-fra.com

Angular 2 Animation vers le haut et vers le bas

J'ai récemment construit le composant Angular 2 Read More. Ce composant fait réduire et développer de longs blocs de texte avec des liens "Read more" et "Read Less". Pas sur la base du nombre de caractères mais sur la base de la hauteur maximale spécifiée.

import { Component, Input, ElementRef, AfterViewInit } from '@angular/core';

@Component({
    selector: 'read-more',
    template: `
        <div [innerHTML]="text" [class.collapsed]="isCollapsed" [style.height]="isCollapsed ? maxHeight+'px' : 'auto'">
        </div>
            <a *ngIf="isCollapsable" (click)="isCollapsed =! isCollapsed">Read {{isCollapsed? 'more':'less'}}</a>
    `,
    styles: [`
        div.collapsed {
            overflow: hidden;
        }
    `]
})
export class ReadMoreComponent implements AfterViewInit {

    //the text that need to be put in the container
    @Input() text: string;

    //maximum height of the container
    @Input() maxHeight: number = 100;

    //set these to false to get the height of the expended container 
    public isCollapsed: boolean = false;
    public isCollapsable: boolean = false;

    constructor(private elementRef: ElementRef) {
    }

    ngAfterViewInit() {
        let currentHeight = this.elementRef.nativeElement.getElementsByTagName('div')[0].offsetHeight;
       //collapsable only if the contents make container exceed the max height
        if (currentHeight > this.maxHeight) {
            this.isCollapsed = true;
            this.isCollapsable = true;
        }
    }
}

Et utilisé comme:

<read-more [text]="details" [maxHeight]="250"></read-more>

Le composant fonctionne bien. Maintenant, je dois ajouter une animation de diapositive haut/bas au composant de sorte que lorsque vous cliquez sur le lien En savoir plus, le contenu glisse vers le bas et lorsque vous cliquez sur Lire moins, le contenu glisse jusqu'à la hauteur maximale spécifiée.

Quelqu'un peut-il nous indiquer comment y parvenir?

9
Naveed Ahmed

Ma solution avec :enter, :leave et *ngIf:

@Component({
    selector: 'accordion',
    templateUrl: './accordion.component.html',
    animations: [
        trigger('slideInOut', [
            state('in', style({height: '*', opacity: 0})),
            transition(':leave', [
                style({height: '*', opacity: 1}),

                group([
                    animate(300, style({height: 0})),
                    animate('200ms ease-in-out', style({'opacity': '0'}))
                ])

            ]),
            transition(':enter', [
                style({height: '0', opacity: 0}),

                group([
                    animate(300, style({height: '*'})),
                    animate('400ms ease-in-out', style({'opacity': '1'}))
                ])

            ])
        ])
    ]
})
...

Modèle:

<div *ngIf="shown" [@slideInOut] >
    // ...content
</div>

Malheureusement, j'ai également dû intégrer ce correctif (pour slideOut): https://github.com/angular/angular/issues/15798

14
Felix

Calcul automatique des propriétés

Animation avec calcul automatique de la hauteur

Parfois, vous ne connaissez la valeur d'une propriété de style dimensionnel qu'au moment de l'exécution. Par exemple, les éléments ont souvent des largeurs et des hauteurs qui dépendent de leur contenu et de la taille de l'écran. Ces propriétés sont souvent difficiles à animer avec CSS.

Dans ces cas, vous pouvez utiliser une valeur de propriété * spéciale pour que la valeur de la propriété soit calculée au moment de l'exécution, puis connectée à l'animation.

Dans cet exemple, l'animation de congé prend la hauteur que l'élément a avant de quitter et s'anime de cette hauteur à zéro:

animations: [
  trigger('shrinkOut', [
    state('in', style({height: '*'})),
    transition('* => void', [
      style({height: '*'}),
      animate(250, style({height: 0}))
    ])
  ])
]

de Angular documentation officielle (maintenant sous forme d'archive): https://v2.angular.io/docs/ts/latest/guide/animations.html#!#automatic- propriété-calcul

14
Julien

La réponse de Threeve est correcte - le seul problème est que la transition CSS ne fonctionnera pas avec 'auto'. Vous devez donc capturer la hauteur automatique dans la fonction ngAfterViewInit et la stocker sous forme de chaîne. Notez également l'utilisation de la fonction setTimeout pour arrêter "l'erreur de violation de flux de données unidirectionnelle" qui peut se produire.

import { Component, Input, ElementRef, AfterViewInit } from '@angular/core';

@Component({
selector: 'read-more',
template: `
    <div [style.height]="isCollapsed ? maxHeight+'px' : autoHeight">
        <ng-content></ng-content>
    </div> 

    <span *ngIf="isCollapsable" class="btn-link cpointer" (click)="isCollapsed =! isCollapsed">Read {{isCollapsed? 'more':'less'}} ...</span>

    `,
styles: [` div { overflow-y: hidden; 
          -moz-transition: height .5s;
          -ms-transition: height .5s;
          -o-transition: height .5s;
          -webkit-transition: height .5s;
          transition: height .5s; ease;}
         .cpointer {cursor:pointer; }
        `]
})
export class ReadMoreComponent implements AfterViewInit {

@Input()
maxHeight: number = 40; //two lines

////set these to false to get the height of the expended container 
isCollapsed: boolean = false;
isCollapsable: boolean = false;
autoHeight: string= "auto";

constructor(private elementRef: ElementRef) {}

ngAfterViewInit() {
    // Inportant !!
    // wait a tick to avoid one-time devMode
    // unidirectional-data-flow-violation error
    setTimeout(_ => {
            let currentHeight = this.elementRef.nativeElement.getElementsByTagName('div')[0].offsetHeight;

            this.autoHeight = currentHeight + "px";
            //collapsable only if the contents make container exceed the max height
            if (currentHeight >= this.maxHeight) {
                this.isCollapsed = true;
                this.isCollapsable = true;
            }
        }
    );
}

}
2
Ron Brill

C'est ce que j'utilise dans Angular 8.1.2. La beauté du code est qu'il prend en charge une hauteur illimitée du div qui doit être affiché/réduit et effectue également des transitions en douceur.

FICHIER TS:

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

@Component({
    selector: 'app-all-data',
    templateUrl: './all-data.page.html',
    styleUrls: ['./all-data.page.scss'],
    animations: [
        trigger('openClose', [
            state('open', style({
                height: '*',
                opacity: 1,
            })),
            state('closed', style({
                height: '0',
                opacity: 0
            })),
            transition('open => closed', [
                animate('0.35s')
            ]),
            transition('closed => open', [
                animate('0.35s')
            ]),
        ]),
    ]
})
export class AllDataPage implements OnInit {

    showCardBody = false;

    constructor() {
    }

    ngOnInit() {
    }

    /**
     * Toggle details on click
     */
    showDetails() {
        this.showCardBody = !this.showCardBody;
    }

}

FICHIER HTML:

<button type="button" (click)="showDetails()">
       Toggle Details
</button>

<div class="card-body" [@openClose]="showCardBody ? 'open' : 'closed'">
       <p>This is some content</p>
       <p>This is some content</p>
       <p>This is some content</p>       
       <p>This is some content</p>
       <p>This is some content</p>
</div>
1
Devner