Angular 4 - 当div进入视口时如何触发动画?

问题描述 投票:7回答:3

我一直在使用Angular 4构建一个新站点,我正在尝试重新创建一个效果,当div变为可见时(向下滚动屏幕)然后可以触发角度动画以滑动div形式双方。

我以前在Angular 4之外使用jQuery已经能够做到这一点,但我想尝试使用原生的Angular 4动画创建相同的效果。

任何人都可以向我提供有关如何在div进入视图时触发动画的建议(即,当它进入视口时向下滚动到页面的下半部分?)。我已经编写了幻灯片动画但我不知道如何在以后将div显示到视口时使用滚动触发它。

感谢大家!

angular animation scroll visible
3个回答
9
投票

我创建了一个指令,只要元素完全在视图中或者它的上边缘已到达视图的上边缘,就会发出一个事件。

这是一个吸烟者: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();
  }
}

2
投票

我已经创建了一个基本组件,它提供了一个标志nowOnce,如果组件完全在视图中或者它的上边缘已经到达视图的上边缘,则会变为true。

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

您可以简单地扩展此组件并通过继承来使用appearOnce属性

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

如果需要覆盖构造函数或生命周期钩子,请记住调用super()。

(编辑)plunker:https://embed.plnkr.co/yIpA1mI1b9kVoEXGy6Hh/

(编辑)我在下面的另一个答案中把它变成了一个指令。


0
投票

如果您想在特定组件中使用它,这是一种简单的方法:

@ViewChild('chatTeaser') chatTeaser: ElementRef;

@HostListener('window:scroll')
checkScroll() {
    const scrollPosition = window.pageYOffset + window.innerHeight;

    if (this.chatTeaser && this.chatTeaser.nativeElement.offsetTop >= scrollPosition) {
        this.animateAvatars();
    }
}

并在HTML中:

<div id="chat-teaser" #chatTeaser>

正好当元素的顶部滚动到该函数时被调用。如果要仅在完整div处于视图中时调用该函数,请将div高度添加到this.chatTeaser.nativeElement.offsetTop

© www.soinside.com 2019 - 2024. All rights reserved.