ng-ytl-zorro-antd
Version:
An enterprise-class UI components based on Ant Design and Angular
149 lines (126 loc) • 3.99 kB
text/typescript
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';
export class NzAffixComponent implements OnChanges, OnInit, OnDestroy, AfterViewInit {
private didScroll = false;
private scrollTime: number = null;
private scroll$: Subscription = null;
private scrollWinInTarget$: Subscription = null;
private wrap: ElementRef;
// 缓存固定状态
private fixed = false;
// 原始位置
private orgOffset: { top: number, left: number };
nzTarget: Element;
nzOffsetTop = 0;
nzOffsetBottom = 0;
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();
}
}