@piumaz/pull-to-refresh
Version:
It provides an Angular component and a service, for the Pull-To-Refresh feature.
247 lines (240 loc) • 10.3 kB
JavaScript
import { ɵɵdefineInjectable, Injectable, EventEmitter, Component, ChangeDetectionStrategy, ChangeDetectorRef, Inject, Input, Output, HostListener, NgModule } from '@angular/core';
import { Subject } from 'rxjs';
import { DOCUMENT, CommonModule } from '@angular/common';
class PullToRefreshService {
constructor() {
this.refresh = new Subject();
this.reset = new Subject();
}
refresh$() {
return this.refresh.asObservable();
}
pull() {
this.refresh.next(true);
}
reset$() {
return this.reset.asObservable();
}
dismiss() {
this.reset.next(true);
}
}
PullToRefreshService.ɵprov = ɵɵdefineInjectable({ factory: function PullToRefreshService_Factory() { return new PullToRefreshService(); }, token: PullToRefreshService, providedIn: "root" });
PullToRefreshService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
PullToRefreshService.ctorParameters = () => [];
class PullToRefreshComponent {
constructor(refreshService, changeDetectorRef, document) {
this.refreshService = refreshService;
this.changeDetectorRef = changeDetectorRef;
this.document = document;
/**
* Spostamento in pixel che attiva il refresh
*/
this.pullToRefresh = 90;
this.color = '#353535';
this.target = 'body';
this.disabled = false;
this.autoDismiss = true;
this.refresh = new EventEmitter();
/**
* Stato attivazione
*/
this.activated = false;
/**
* Rotazione dell'icon spinner
*/
this.rotation = 0;
this.spin = false;
/**
* posizione Y di inizio sul touchstart
*/
this.startY = 0;
/**
* posizione X di inizio sul touchstart
*/
this.startX = 0;
/**
* posizione Y del touchmove
*/
this.moveY = 0;
/**
* posizione X del touchmove
*/
this.moveX = 0;
/**
* Spostamento in pixel dello spinner
*/
this.pull = 0;
this.pullFirst = 0;
/**
* Spostamento massimo in pixel dello spinner
*/
this.maxPull = 138;
this.maxFirstPull = 60;
/**
* posizione Y dell'animazione finale
*/
this.animateY = 80;
this.radiusLeft = 0;
this.radiusRight = 0;
this.isFirstTime = true;
this.window = this.document.defaultView;
}
ngOnInit() {
this.elementScrollable = this.document.querySelector(this.target);
if (!this.autoDismiss) {
this.resetSub = this.refreshService.reset$().subscribe(() => {
this.dismiss();
});
}
}
ngOnDestroy() {
if (this.resetSub) {
this.resetSub.unsubscribe();
}
}
getScrollTop() {
if (this.target === 'body') {
return this.window.pageYOffset || this.document.documentElement.scrollTop || this.document.body.scrollTop || 0;
}
return this.elementScrollable.scrollTop;
}
onTouchStart($e) {
if (this.disabled || this.activated) {
return;
}
this.reset();
this.startY = $e.touches[0].pageY;
this.startX = $e.touches[0].pageX;
}
onToucMove($e) {
if (this.disabled) {
return;
}
this.moveY = $e.touches[0].pageY;
this.moveX = $e.touches[0].pageX;
if (this.getScrollTop() > 0) {
this.isFirstTime = true;
}
const shiftY = (this.moveY - this.startY);
const shiftX = (this.moveX - this.startX);
const ratio = Math.abs(shiftX) / Math.abs(shiftY);
if (this.getScrollTop() === 0 && this.moveY >= this.startY && ratio <= 0.3) {
setTimeout(() => {
this.elementScrollable.style.overflowY = 'hidden';
});
if (this.isFirstTime) {
this.pullFirst = shiftY >= this.maxFirstPull ? this.maxFirstPull : shiftY;
const width = this.elementScrollable.offsetWidth;
const x = parseInt(((100 * this.moveX) / width).toString(), 10);
let left;
let right;
if (x <= 50) {
// left
right = 100 - x;
left = x;
}
else {
// right
right = 100 - x;
left = 100 - (100 - x);
}
this.radiusLeft = left < 0 ? 0 : left;
this.radiusRight = right < 0 ? 0 : right;
}
else {
const pullShiftY = shiftY / 2;
this.pull = (pullShiftY >= this.maxPull) ? this.maxPull : pullShiftY + ((this.maxPull - pullShiftY) * 0.5);
this.rotation = (360 * this.pull) / this.maxPull;
this.activated = (this.pull >= this.pullToRefresh);
}
}
}
onTouchEnd($e) {
if (this.disabled) {
return;
}
if (this.activated) {
this.spin = true;
this.pull = this.animateY;
this.document.dispatchEvent(new Event('pull-to-refresh'));
this.refreshService.pull();
this.refresh.emit();
if (this.autoDismiss) {
this.dismiss();
}
}
else {
this.reset();
}
}
dismiss() {
setTimeout(() => {
this.reset();
}, 1500);
}
reset() {
this.elementScrollable.style.overflowY = '';
this.isFirstTime = false;
this.startY = 9999999;
this.moveY = 0;
this.rotation = 0;
this.spin = false;
this.activated = false;
this.pull = 0;
this.pullFirst = 0;
this.changeDetectorRef.detectChanges();
}
}
PullToRefreshComponent.decorators = [
{ type: Component, args: [{
selector: 'pull-to-refresh',
template: "\r\n<div class=\"pull\" [ngStyle]=\"{'transform': 'translateY(' + pull + 'px)'}\" [hidden]=\"disabled || pullFirst\">\r\n\r\n <span class=\"rotate\" [ngStyle]=\"{'transform': 'rotate(' + rotation + 'deg)'}\">\r\n\r\n <span class=\"spinner\" [class.spin-animation]=\"spin\" [ngStyle]=\"{'opacity': (rotation/360) - 0.1}\">\r\n\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"xMidYMid\">\r\n <g>\r\n <path d=\"M50 15A35 35 0 1 0 74.74873734152916 25.251262658470843\" fill=\"none\" [attr.stroke]=\"color\" stroke-width=\"10\" ></path>\r\n <path d=\"M49 3L49 27L61 15L49 3\" [attr.fill]=\"color\" [attr.stroke]=\"color\" stroke-width=\"4\"></path>\r\n </g>\r\n </svg>\r\n\r\n </span>\r\n\r\n </span>\r\n\r\n</div>\r\n\r\n<div class=\"pull first\"\r\n [ngStyle]=\"{'transform': 'translateY(' + pullFirst + 'px)', 'border-bottom-right-radius': radiusRight + '%', 'border-bottom-left-radius': radiusLeft + '%'}\"\r\n [hidden]=\"disabled || pull\"></div>\r\n",
changeDetection: ChangeDetectionStrategy.OnPush,
styles: ["::ng-deep body,::ng-deep html{overscroll-behavior-y:none}.pull{display:flex;justify-content:space-around;align-items:center;position:absolute;z-index:1060;width:100%;padding:0;left:0;right:0;text-align:center;overflow:hidden;transform:translateY(-60px);transition:transform .2s;will-change:transform,opacity}.pull,.pull.first{height:60px;top:-60px}.pull.first{transition:transform .3s;background-color:rgba(53,53,53,.3);opacity:.5;border-radius:0/0 0 0 0}.pull .rotate{transition:transform .3s;will-change:transform,opacity;background-color:#fff;border:1px solid rgba(53,53,53,.3);box-shadow:0 0 4px 0 rgba(0,0,0,.2);border-radius:100px;padding:6px;box-sizing:content-box}.pull .rotate,.pull .spinner{height:25px;width:25px;display:inline-block}.pull .spinner svg{height:100%;width:100%}.pull .spinner.spin-animation{-webkit-animation-name:spin;animation-name:spin;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-timing-function:linear;animation-timing-function:linear}@-webkit-keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}"]
},] }
];
PullToRefreshComponent.ctorParameters = () => [
{ type: PullToRefreshService },
{ type: ChangeDetectorRef },
{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
];
PullToRefreshComponent.propDecorators = {
pullToRefresh: [{ type: Input, args: ['sensitivity',] }],
color: [{ type: Input }],
target: [{ type: Input }],
disabled: [{ type: Input }],
autoDismiss: [{ type: Input }],
refresh: [{ type: Output }],
onTouchStart: [{ type: HostListener, args: ['window:touchstart', ['$event'],] }],
onToucMove: [{ type: HostListener, args: ['window:touchmove', ['$event'],] }],
onTouchEnd: [{ type: HostListener, args: ['window:touchend', ['$event'],] }, { type: HostListener, args: ['window:touchcancel', ['$event'],] }]
};
class PullToRefreshModule {
}
PullToRefreshModule.decorators = [
{ type: NgModule, args: [{
declarations: [
PullToRefreshComponent
],
imports: [
CommonModule
],
exports: [
CommonModule,
PullToRefreshComponent
]
},] }
];
/*
* Public API Surface of pull-to-refresh
*/
/**
* Generated bundle index. Do not edit.
*/
export { PullToRefreshComponent, PullToRefreshModule, PullToRefreshService };
//# sourceMappingURL=piumaz-pull-to-refresh.js.map