直接上代碼,代碼中有詳細註釋!演示地址
import {
Component,
OnInit,
ElementRef,
Renderer2,
ViewChild,
OnDestroy,
AfterViewInit,
Input,
HostBinding,
EventEmitter,
Output
} from "@angular/core";
import * as RxCSS from "rxcss";
import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";
import { fromEvent } from "rxjs/observable/fromEvent";
@Component({
selector: "scroll-view",
templateUrl: "./scroll-view.component.html",
styleUrls: ["./scroll-view.component.scss"]
})
export class ScrollViewComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChild("scroller") scroller: ElementRef;
@ViewChild("content") content: ElementRef;
animation$: Subject<{ x: number; y: number; z: number }> = new Subject();
style$: any;
isPullUp: boolean = false;
isPullDown: boolean = false;
scrollerSize: { width: number; height: number } = {
width: 0,
height: 0
};
contentSize: { width: number; height: number } = {
width: 0,
height: 0
};
@Input() upDistance: number = 80;
@Input() downDistance: number = 80;
@Input() timeLimit: number = 2000;
@HostBinding("style.height.px")
@Input()
height: number;
@Output() loadMore: EventEmitter<any> = new EventEmitter();
@Output() onRefresh: EventEmitter<any> = new EventEmitter();
timer: any;
constructor(public ele: ElementRef) {}
ngOnInit() {
this.height = this.ele.nativeElement.clientHeight;
this.style$ = RxCSS(
{ mouse: this.animation$.asObservable() },
this.scroller.nativeElement
).subscribe(res => {});
fromEvent(this.scroller.nativeElement, "scroll").subscribe(res => {
if (this.isPullDown || this.isPullUp) {
this.reset();
}
let scrollTop = this.scroller.nativeElement.scrollTop;
let scrollHeight = this.scroller.nativeElement.scrollHeight;
let clientHeight = this.scroller.nativeElement.clientHeight;
let toBottomDistance = scrollHeight - scrollTop - clientHeight;
if (toBottomDistance < this.downDistance) {
this.switchPullUp();
this.loadMore.emit();
}
if (scrollTop < this.upDistance) {
this.switchPullDown();
this.onRefresh.emit();
}
});
}
getSize() {
this.scrollerSize = {
width: this.scroller.nativeElement.clientWidth,
height: this.scroller.nativeElement.clientHeight
};
this.contentSize = {
width: this.content.nativeElement.clientWidth,
height: this.content.nativeElement.clientHeight
};
}
ngOnDestroy() {
this.animation$.unsubscribe();
this.style$.unsubscribe();
}
ngAfterViewInit() {
this.getSize();
setTimeout(() => {
this.switchPullDown();
}, 0);
}
switchPullUp() {
if (this.isPullUp) {
this.reset();
} else {
this.isPullUp = true;
this.animation$.next({
x: 0,
y: -this.upDistance,
z: 0
});
this.setTimer();
}
}
switchPullDown() {
if (this.isPullDown) {
this.reset();
} else {
this.isPullDown = true;
this.animation$.next({
x: 0,
y: this.downDistance,
z: 0
});
this.setTimer();
}
}
reset() {
this.isPullUp = false;
this.isPullDown = false;
this.animation$.next({
x: 0,
y: 0,
z: 0
});
}
setTimer() {
this.timer = setTimeout(() => {
this.reset();
}, this.timeLimit);
}
}
複製代碼
- scroll-view.component.html
<div class="scroll-refresh">
下拉刷新
</div>
<div class="scroller" #scroller>
<div class="scroller-content" #content>
<ng-content></ng-content>
</div>
</div>
<div class="scroll-loadmore">
上拉加載
</div>
複製代碼
- scroll-view.component.scss
:host {
display: block;
position: relative;
overflow: hidden;
z-index: 0;
height: 100%;
}
.scroller {
position: relative;
overflow: auto;
-webkit-overflow-scrolling: touch;
max-height: 100%;
transition: transform 0.5s cubic-bezier(0.165, 0.84, 0.44, 1);
transform: translateX(calc(var(--mouse-x) * 1px)) translateY(calc(var(--mouse-y) * 1px)) translateY(calc(var(--mouse-z) * 1px));
background: #efefef;
z-index: 2;
.scroller-content {
height: 100%;
position: relative;
}
}
.scroll-loadmore,
.scroll-refresh {
position: absolute;
left: 0px;
right: 0px;
height: 80px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.scroll-refresh {
top: 0px;
}
.scroll-loadmore {
bottom: 0px;
}
複製代碼