UNPKG

@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
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