2017-04-23 5 views
3

J'ai construit un nouveau site en utilisant Angular 4 et j'essaye de recréer un effet où quand un div devient visible (quand vous faites défiler l'écran) alors cela peut alors déclencher une animation angulaire pour faire glisser la div en forme les côtés.Angular 4 - comment déclencher une animation quand un div entre dans la fenêtre d'affichage?

J'ai été capable de le faire dans le passé en utilisant jQuery en dehors de Angular 4 mais je veux essayer de créer le même effet en utilisant des animations Angular 4 natives.

Est-ce que quelqu'un peut me donner des conseils sur la façon de déclencher une animation quand un div apparaît (c'est-à-dire défiler vers le bas de la page quand il entre dans la fenêtre?). J'ai déjà écrit les animations de diapositives mais je ne sais pas comment déclencher cela avec un défilement quand un div devient visible à une date ultérieure au port de vue.

Merci à tous!

+0

Je ne sais pas, mais ce lien peut être utile https://angular.io /docs/ts/latest/guide/animations.html#!#parallel-animation-groups? – sandyJoshi

+0

Salut Sandy, j'ai jeté un coup d'œil à ça et l'approche des groupes d'animation parallèles aide les animations en chaîne mais il ne semble pas possible de se déclencher quand un div entre dans le point de vue. un temps variable lorsque l'utilisateur décide de faire défiler jusqu'à la div. Connaissez-vous une solution à ce comportement de l'interface utilisateur? – Ultronn

Répondre

4

J'ai créé une directive qui émet un événement dès que l'élément est entièrement dans la vue ou que son bord supérieur a atteint le bord supérieur de la vue.

Voici un plunker: https://embed.plnkr.co/mlez1dXjR87FNBHXq1YM/

Il est utilisé comme ceci:

<div (appear)="onAppear()">...</div> 

est ici la directive:

import { 
    ElementRef, Output, Directive, AfterViewInit, OnDestroy, EventEmitter 
} from '@angular/core'; 
import {Observable} from 'rxjs/Observable'; 
import {Subscription} from 'rxjs/Subscription'; 
import 'rxjs/add/observable/fromEvent'; 
import 'rxjs/add/operator/startWith'; 

@Directive({ 
    selector: '[appear]' 
}) 
export class AppearDirective implements AfterViewInit, OnDestroy { 
    @Output() 
    appear: EventEmitter<void>; 

    elementPos: number; 
    elementHeight: number; 

    scrollPos: number; 
    windowHeight: number; 

    subscriptionScroll: Subscription; 
    subscriptionResize: Subscription; 

    constructor(private element: ElementRef){ 
    this.appear = new EventEmitter<void>(); 
    } 

    saveDimensions() { 
    this.elementPos = this.getOffsetTop(this.element.nativeElement); 
    this.elementHeight = this.element.nativeElement.offsetHeight; 
    this.windowHeight = window.innerHeight; 
    } 
    saveScrollPos() { 
    this.scrollPos = window.scrollY; 
    } 
    getOffsetTop(element: any){ 
    let offsetTop = element.offsetTop || 0; 
    if(element.offsetParent){ 
     offsetTop += this.getOffsetTop(element.offsetParent); 
    } 
    return offsetTop; 
    } 
    checkVisibility(){ 
    if(this.isVisible()){ 
     // double check dimensions (due to async loaded contents, e.g. images) 
     this.saveDimensions(); 
     if(this.isVisible()){ 
     this.unsubscribe(); 
     this.appear.emit(); 
     } 
    } 
    } 
    isVisible(){ 
    return this.scrollPos >= this.elementPos || (this.scrollPos + this.windowHeight) >= (this.elementPos + this.elementHeight); 
    } 

    subscribe(){ 
    this.subscriptionScroll = Observable.fromEvent(window, 'scroll').startWith(null) 
     .subscribe(() => { 
     this.saveScrollPos(); 
     this.checkVisibility(); 
     }); 
    this.subscriptionResize = Observable.fromEvent(window, 'resize').startWith(null) 
     .subscribe(() => { 
     this.saveDimensions(); 
     this.checkVisibility(); 
     }); 
    } 
    unsubscribe(){ 
    if(this.subscriptionScroll){ 
     this.subscriptionScroll.unsubscribe(); 
    } 
    if(this.subscriptionResize){ 
     this.subscriptionResize.unsubscribe(); 
    } 
    } 

    ngAfterViewInit(){ 
    this.subscribe(); 
    } 
    ngOnDestroy(){ 
    this.unsubscribe(); 
    } 
} 
+0

pour ie11 compatibilité vous pouvez changer window.scrollY en document.documentElement.scrollTop. –

+0

cela ne fonctionne pas correctement avec plusieurs éléments sur la page – godblessstrawberry

2

J'ai créé un composant de base qui fournit un indicateur appearOnce, qui devient vrai une fois si le composant est complètement dans la vue ou si son bord supérieur a atteint le bord supérieur de la vue.

@Injectable() 
export class AppearOnce implements AfterViewInit, OnDestroy { 
    appearedOnce: boolean; 

    elementPos: number; 
    elementHeight: number; 

    scrollPos: number; 
    windowHeight: number; 

    subscriptionScroll: Subscription; 
    subscriptionResize: Subscription; 

    constructor(private element: ElementRef, private cdRef: ChangeDetectorRef){} 
    onResize() { 
    this.elementPos = this.getOffsetTop(this.element.nativeElement); 
    this.elementHeight = this.element.nativeElement.clientHeight; 
    this.checkVisibility(); 
    } 
    onScroll() { 
    this.scrollPos = window.scrollY; 
    this.windowHeight = window.innerHeight; 
    this.checkVisibility(); 
    } 
    getOffsetTop(element: any){ 
    let offsetTop = element.offsetTop || 0; 
    if(element.offsetParent){ 
     offsetTop += this.getOffsetTop(element.offsetParent); 
    } 
    return offsetTop; 
    } 

    checkVisibility(){ 
    if(!this.appearedOnce){ 
     if(this.scrollPos >= this.elementPos || (this.scrollPos + this.windowHeight) >= (this.elementPos + this.elementHeight)){ 
     this.appearedOnce = true; 
     this.unsubscribe(); 
     this.cdRef.detectChanges(); 
     } 
    } 
    } 

    subscribe(){ 
    this.subscriptionScroll = Observable.fromEvent(window, 'scroll').startWith(null) 
     .subscribe(() => this.onScroll()); 
    this.subscriptionResize = Observable.fromEvent(window, 'resize').startWith(null) 
     .subscribe(() => this.onResize()); 
    } 
    unsubscribe(){ 
    if(this.subscriptionScroll){ 
     this.subscriptionScroll.unsubscribe(); 
    } 
    if(this.subscriptionResize){ 
     this.subscriptionResize.unsubscribe(); 
    } 
    } 

    ngAfterViewInit(){ 
    this.subscribe(); 
    } 
    ngOnDestroy(){ 
    this.unsubscribe(); 
    } 
} 

Vous pouvez simplement étendre ce composant et utiliser la appearedOnce propriété par héritage

@Component({ 
    template: ` 
    <div> 
     <div *ngIf="appearedOnce">...</div> 
     ... 
    </div> 
    ` 
}) 
class MyComponent extends AppearOnceComponent { 
    ... 
} 

Gardez à l'esprit pour appeler super() si vous avez besoin d'écraser le constructeur ou lifecyclehooks.

(modifier) ​​plunker: https://embed.plnkr.co/yIpA1mI1b9kVoEXGy6Hh/

(modifier) ​​Je l'ai transformé cela en une directive dans une autre réponse ci-dessous.

+0

Bonjour Martin, C'est une solution vraiment intéressante. Pourriez-vous décrire comment implémenter la même logique dans un composant existant et quelles importations sont nécessaires pour le composant. Je suis relativement nouveau à angulaire et serais vraiment heureux des détails sur la mise en œuvre. Merci. – Ultronn

+0

j'ai créé un peu plnkr: https://embed.plnkr.co/yIpA1mI1b9kVoEXGy6Hh/ composant Il y a un exemple (TurnGreenWhenInViewComponent) qui étend AppearOnce et utilise son drapeau appearedOnce passe au vert après il est apparu une fois. –

+0

Merci beaucoup! merci Martin. – Ultronn