Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

scrollspy-style plugin for animations #127

Open
xmlking opened this issue Feb 7, 2017 · 1 comment
Open

scrollspy-style plugin for animations #127

xmlking opened this issue Feb 7, 2017 · 1 comment

Comments

@xmlking
Copy link

xmlking commented Feb 7, 2017

I like to submit scrollspy-style plugin that adds a css class if the element is in viewport.
It can be used for adding scroll animations to elements or hide/show navbar.

import {
  Directive, EventEmitter, Output, OnDestroy, ElementRef, Input, AfterViewInit, Inject,
  Renderer, OnInit
} from '@angular/core';

import {Subscription} from 'rxjs/Subscription';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/debounceTime';
import {ScrollSpyService} from 'ng2-scrollspy';
import {WindowService} from '../../../core/services/window.service';
import {DOCUMENT} from '@angular/platform-browser';

declare var $: any;


export interface ScrollSpyStyleOptions {
  className?: string;
  debounce?: number;
  cushion?: number;
  fullyInView?: boolean;
}
@Directive({
  selector: '[scrollSpyStyle]',
})
export class ScrollSpyStyleDirective implements OnInit, OnDestroy, AfterViewInit {

  private sub: Subscription;
  private element: HTMLElement;

  @Input('scrollSpyStyle') public options: ScrollSpyStyleOptions;
  @Output() onVisibilityChange: EventEmitter<boolean> = new EventEmitter();

  private defaultOptions: ScrollSpyStyleOptions = {
    className: 'appeared',
    debounce: 0,
    cushion: 0,
    fullyInView: false
  };

  constructor(private scrollSpyService: ScrollSpyService, elementRef: ElementRef,
              @Inject(DOCUMENT) private _document: Document, private renderer: Renderer,
              @Inject(WindowService) private _window: Window) {
    this.element = elementRef.nativeElement;
  }

  ngOnInit() {
    if (!this.options) {
      this.options = this.defaultOptions;
    } else {
      this.options = Object.assign(this.defaultOptions, this.options);
    }
  }

  ngAfterViewInit() {
    this.sub = this.scrollSpyService.getObservable('window')
      // .debounce(this.options.debounce)
      .map(() => this.isElementInViewport())
      .distinctUntilChanged()
      .subscribe((inView: boolean) => {
        console.log('inView', inView);
        this.onVisibilityChange.emit(inView);
        this.renderer.setElementClass(this.element, this.options.className, inView);
      });
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }

  private isElementInViewport() {
    let rect = this.element.getBoundingClientRect();
    const html = this._document.documentElement;

    if (this.options.cushion !== 0) {
      rect = this.addCushion(rect, this.options.cushion);
    }
    if (this.options.fullyInView === true) {
      return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (this._window.innerHeight || html.clientHeight) &&
        rect.right <= (this._window.innerWidth || html.clientWidth)
      );
    } else {
      return (
        rect.bottom >= 0 &&
        rect.right >= 0 &&
        rect.top <= (this._window.innerHeight || html.clientHeight) &&
        rect.left <= (this._window.innerWidth || html.clientWidth)
      );
    }
  }

  /**
   *  If a cushion is specified, the properties are adjusted according to the cushion amount.
   *  If the cushion is positive the rectangle will represent an area that is larger that the actual element.
   *  If the cushion is negative then the rectangle will represent an area that is smaller that the actual element.
   */
  private addCushion(rect: ClientRect, cushion: number) {
    return {
      right: rect.right + cushion,
      left: rect.left - cushion,
      top: rect.top - cushion,
      bottom: rect.bottom + cushion,
      get width() {return this.right - this.left; },
      get height() {return this.bottom - this.top; },
    };
  }
}
@JonnyBGod
Copy link
Owner

Cool thanks.

Could you provide a PR for this plugin?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants