UNPKG

ng-ytl-zorro-antd

Version:

An enterprise-class UI components based on Ant Design and Angular

149 lines (126 loc) 3.99 kB
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChange, SimpleChanges, ViewChild, ViewEncapsulation, } from '@angular/core'; import { Subscription } from 'rxjs/Subscription'; import { fromEvent } from 'rxjs/observable/fromEvent'; import { distinctUntilChanged } from 'rxjs/operators/distinctUntilChanged'; import { throttleTime } from 'rxjs/operators/throttleTime'; import { NzScrollService } from '../core/scroll/nz-scroll.service'; @Component({ selector : 'nz-affix', encapsulation: ViewEncapsulation.None, template : ` <div #wrap> <ng-content></ng-content> </div>`, styleUrls : [ './style/index.less', './style/patch.less' ] }) export class NzAffixComponent implements OnChanges, OnInit, OnDestroy, AfterViewInit { private didScroll = false; private scrollTime: number = null; private scroll$: Subscription = null; private scrollWinInTarget$: Subscription = null; @ViewChild('wrap') private wrap: ElementRef; // 缓存固定状态 private fixed = false; // 原始位置 private orgOffset: { top: number, left: number }; @Input() nzTarget: Element; @Input() nzOffsetTop = 0; @Input() nzOffsetBottom = 0; @Output() nzChange: EventEmitter<boolean> = new EventEmitter(); constructor(private scrollSrv: NzScrollService, private _el: ElementRef) { } ngOnChanges(changes: { [P in keyof this]?: SimpleChange } & SimpleChanges): void { if (changes.nzTarget) { this.registerScrollEvent(); } } ngOnInit(): void { if (!this.nzTarget) { this.registerScrollEvent(); } } ngAfterViewInit(): void { this.orgOffset = null; this.fixed = false; } private reCalculate(): this { const elOffset = this.scrollSrv.getOffset(this._el.nativeElement); this.orgOffset = { top : elOffset.top + this.scrollSrv.getScroll(this.nzTarget), left: elOffset.left + this.scrollSrv.getScroll(this.nzTarget, false) }; return this; } private process(): void { if (!this.orgOffset) { this.reCalculate(); } const containerScrollTop = this.scrollSrv.getScroll(this.nzTarget); const fixTop = this.nzTarget ? this.scrollSrv.getOffset(this.nzTarget).top : 0; const hasFixed = this.orgOffset.top - fixTop - containerScrollTop - this.nzOffsetTop <= 0; if (this.fixed === hasFixed) { return; } const wrapEl = this.wrap.nativeElement; wrapEl.classList[ hasFixed ? 'add' : 'remove' ]('ant-affix'); if (hasFixed) { wrapEl.style.cssText = `top:${((+this.nzOffsetTop) + (+fixTop))}px;left:${this.orgOffset.left}px`; } else { wrapEl.style.cssText = ``; } this.fixed = hasFixed; this.nzChange.emit(hasFixed); } private removeListen(): void { if (this.scrollTime) { clearTimeout(this.scrollTime); } if (this.scroll$) { this.scroll$.unsubscribe(); } if (this.scrollWinInTarget$) { this.scrollWinInTarget$.unsubscribe(); } } private registerScrollEvent(): void { this.removeListen(); this.reCalculate().process(); // TODO: should refactor this logic this.scrollTime = window.setInterval(() => { if (this.didScroll) { this.didScroll = false; this.process(); } }, 100); this.scroll$ = fromEvent(this.nzTarget || window, 'scroll') .subscribe(() => this.didScroll = true); if (this.nzTarget) { // 当 window 滚动位发生变动时,需要重新计算滚动容器 this.scrollWinInTarget$ = fromEvent(window, 'scroll').pipe(throttleTime(50), distinctUntilChanged()) .subscribe(e => { this.orgOffset = null; this.fixed = false; }); } } ngOnDestroy(): void { this.removeListen(); } }