2017-04-23 83 views
3

我一直在建設一個使用Angular 4的新網站,我試圖重新創建一個效果,當div變成可見的時候(當你向下滾動屏幕時),然後可以觸發一個角度動畫來滑動div在形式方面。Angular 4 - 如何在div進入視口時觸發動畫?

我已經能夠在過去使用jQuery之外的Angular 4做到這一點,但我想嘗試使用本地Angular 4動畫創建相同的效果。

任何人都可以爲我提供關於如何在div進入視圖時觸發動畫的建議(即,當它進入視口時向下滾動到頁面的下半部分?)。我已經寫過幻燈片動畫了,但是我不知道如何通過滾動來觸發它,當一個div在稍後的日期變爲可見的時候可以看到視口。

謝謝大家!

+0

不知道,但可以在這個環節是有幫助的https://angular.io /docs/ts/latest/guide/animations.html#!#parallel-animation-groups? – sandyJoshi

+0

嗨桑迪,我確實看了看,並行動畫組的方法有助於鏈動畫,但它似乎沒有辦法觸發當div進入視點後滾動到頁面的低點可能發生在當使用決定向下滾動到div時的可變時間。你知道這個UI行爲的任何解決方案嗎? – Ultronn

回答

4

我創建了一個指令,只要元素完全處於視圖內或者其上邊緣已經到達視圖的上邊緣,就會發出一個事件。

這裏有一個plunker:https://embed.plnkr.co/mlez1dXjR87FNBHXq1YM/

它這樣使用:

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

這裏的指令:

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

爲ie11兼容性,你可以改變window.scrollY到document.documentElement.scrollTop。 –

+0

這不適用於頁面上的多個元素 – godblessstrawberry

2

我創建了一個基本組件,它提供了一個標誌wasoOnce,如果組件完全位於視圖內或其上邊緣已到達視圖的上邊緣,則該組件將變爲真。

@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(); 
    } 
} 

您可以簡單地擴展這個組件,並通過繼承

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

利用appearedOnce財產記住調用超()如果你需要重寫構造函數或lifecyclehooks。

(編輯)plunkerhttps://embed.plnkr.co/yIpA1mI1b9kVoEXGy6Hh/

(編輯)我已經變成了下面另一個答案一個指令這一點。

+0

嗨馬丁,這是一個非常有趣的解決方案。您能否描述如何在現有組件中實現相同的邏輯以及組件需要哪些導入。我相對較新的角度,並會真正歡迎實施的細節。謝謝。 – Ultronn

+0

我創建了一個小plnkr: https://embed.plnkr.co/yIpA1mI1b9kVoEXGy6Hh/ 這裏有一個例子組件延伸AppearOnce並使用它(TurnGreenWhenInViewComponent)是appearedOnce標誌變綠後出現過一次。 –

+0

非常感謝!謝謝馬丁。 – Ultronn