mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-21 04:53:58 +08:00
fix(): remove duplicate ts files that were not needed.
This commit is contained in:
@ -1,397 +0,0 @@
|
|||||||
import { BlockerDelegate } from './gesture-controller';
|
|
||||||
import { Component, Listen, Prop, Watch } from '@stencil/core';
|
|
||||||
import { Ionic } from '@stencil/core';
|
|
||||||
import { GestureCallback, GestureDetail, GlobalNamespace } from '../../utils/interfaces';
|
|
||||||
import { applyStyles, getElementReference, pointerCoordX, pointerCoordY } from '../../utils/helpers';
|
|
||||||
import { GestureController, GestureDelegate, BLOCK_ALL } from './gesture-controller';
|
|
||||||
import { PanRecognizer } from './recognizers';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
tag: 'ion-gesture'
|
|
||||||
})
|
|
||||||
export class Gesture {
|
|
||||||
private $el: HTMLElement;
|
|
||||||
private ctrl: GestureController;
|
|
||||||
private detail: GestureDetail = {};
|
|
||||||
private positions: number[] = [];
|
|
||||||
private gesture: GestureDelegate;
|
|
||||||
private lastTouch = 0;
|
|
||||||
private pan: PanRecognizer;
|
|
||||||
private hasCapturedPan = false;
|
|
||||||
private hasPress = false;
|
|
||||||
private hasStartedPan = false;
|
|
||||||
private requiresMove = false;
|
|
||||||
private isMoveQueued = false;
|
|
||||||
private blocker: BlockerDelegate;
|
|
||||||
|
|
||||||
@Prop() attachTo: string = 'child';
|
|
||||||
@Prop() autoBlockAll: boolean = false;
|
|
||||||
@Prop() block: string = null;
|
|
||||||
@Prop() disableScroll: boolean = false;
|
|
||||||
@Prop() direction: string = 'x';
|
|
||||||
@Prop() gestureName: string = '';
|
|
||||||
@Prop() gesturePriority: number = 0;
|
|
||||||
@Prop() maxAngle: number = 40;
|
|
||||||
@Prop() threshold: number = 20;
|
|
||||||
@Prop() type: string = 'pan';
|
|
||||||
|
|
||||||
@Prop() canStart: GestureCallback;
|
|
||||||
@Prop() onStart: GestureCallback;
|
|
||||||
@Prop() onMove: GestureCallback;
|
|
||||||
@Prop() onEnd: GestureCallback;
|
|
||||||
@Prop() onPress: GestureCallback;
|
|
||||||
@Prop() notCaptured: GestureCallback;
|
|
||||||
|
|
||||||
|
|
||||||
ionViewDidLoad() {
|
|
||||||
this.ctrl = (<GlobalNamespace>Ionic).controllers.gesture = ((<GlobalNamespace>Ionic).controllers.gesture || new GestureController());
|
|
||||||
|
|
||||||
this.gesture = this.ctrl.createGesture(this.gestureName, this.gesturePriority, this.disableScroll);
|
|
||||||
|
|
||||||
const types = this.type.replace(/\s/g, '').toLowerCase().split(',');
|
|
||||||
|
|
||||||
if (types.indexOf('pan') > -1) {
|
|
||||||
this.pan = new PanRecognizer(this.direction, this.threshold, this.maxAngle);
|
|
||||||
this.requiresMove = true;
|
|
||||||
}
|
|
||||||
this.hasPress = (types.indexOf('press') > -1);
|
|
||||||
|
|
||||||
if (this.pan || this.hasPress) {
|
|
||||||
Ionic.listener.enable(this, 'touchstart', true, this.attachTo);
|
|
||||||
Ionic.listener.enable(this, 'mousedown', true, this.attachTo);
|
|
||||||
|
|
||||||
Ionic.dom.write(() => {
|
|
||||||
applyStyles(getElementReference(this.$el, this.attachTo), GESTURE_INLINE_STYLES);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.autoBlockAll) {
|
|
||||||
this.blocker = this.ctrl.createBlocker(BLOCK_ALL);
|
|
||||||
this.blocker.block();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Watch('block')
|
|
||||||
blockChange(block: string) {
|
|
||||||
if (this.blocker) {
|
|
||||||
this.blocker.destroy();
|
|
||||||
}
|
|
||||||
if (block) {
|
|
||||||
this.blocker = this.ctrl.createBlocker(block.split(','));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DOWN *************************
|
|
||||||
|
|
||||||
@Listen('touchstart', { passive: true, enabled: false })
|
|
||||||
onTouchStart(ev: TouchEvent) {
|
|
||||||
this.lastTouch = now(ev);
|
|
||||||
|
|
||||||
this.enableMouse(false);
|
|
||||||
this.enableTouch(true);
|
|
||||||
|
|
||||||
this.pointerDown(ev, this.lastTouch);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Listen('mousedown', { passive: true, enabled: false })
|
|
||||||
onMouseDown(ev: MouseEvent) {
|
|
||||||
const timeStamp = now(ev);
|
|
||||||
|
|
||||||
if (this.lastTouch === 0 || (this.lastTouch + MOUSE_WAIT < timeStamp)) {
|
|
||||||
this.enableMouse(true);
|
|
||||||
this.enableTouch(false);
|
|
||||||
|
|
||||||
this.pointerDown(ev, timeStamp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private pointerDown(ev: UIEvent, timeStamp: number): boolean {
|
|
||||||
if (!this.gesture || this.hasStartedPan) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const detail = this.detail;
|
|
||||||
|
|
||||||
detail.startX = detail.currentX = pointerCoordX(ev);
|
|
||||||
detail.startY = detail.currentY = pointerCoordY(ev);
|
|
||||||
detail.startTimeStamp = detail.timeStamp = timeStamp;
|
|
||||||
detail.velocityX = detail.velocityY = detail.deltaX = detail.deltaY = 0;
|
|
||||||
detail.directionX = detail.directionY = detail.velocityDirectionX = detail.velocityDirectionY = null;
|
|
||||||
detail.event = ev;
|
|
||||||
this.positions.length = 0;
|
|
||||||
|
|
||||||
if (this.canStart && this.canStart(detail) === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.positions.push(detail.currentX, detail.currentY, timeStamp);
|
|
||||||
|
|
||||||
// Release fallback
|
|
||||||
this.gesture.release();
|
|
||||||
|
|
||||||
// Start gesture
|
|
||||||
if (!this.gesture.start()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.pan) {
|
|
||||||
this.hasStartedPan = true;
|
|
||||||
this.hasCapturedPan = false;
|
|
||||||
|
|
||||||
this.pan.start(detail.startX, detail.startY);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MOVE *************************
|
|
||||||
|
|
||||||
@Listen('touchmove', { passive: true, enabled: false })
|
|
||||||
onTouchMove(ev: TouchEvent) {
|
|
||||||
this.lastTouch = this.detail.timeStamp = now(ev);
|
|
||||||
|
|
||||||
this.pointerMove(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Listen('document:mousemove', { passive: true, enabled: false })
|
|
||||||
onMoveMove(ev: TouchEvent) {
|
|
||||||
const timeStamp = now(ev);
|
|
||||||
|
|
||||||
if (this.lastTouch === 0 || (this.lastTouch + MOUSE_WAIT < timeStamp)) {
|
|
||||||
this.detail.timeStamp = timeStamp;
|
|
||||||
this.pointerMove(ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private pointerMove(ev: UIEvent) {
|
|
||||||
const detail = this.detail;
|
|
||||||
this.calcGestureData(ev);
|
|
||||||
|
|
||||||
if (this.pan) {
|
|
||||||
if (this.hasCapturedPan) {
|
|
||||||
|
|
||||||
if (!this.isMoveQueued) {
|
|
||||||
this.isMoveQueued = true;
|
|
||||||
|
|
||||||
Ionic.dom.write(() => {
|
|
||||||
this.isMoveQueued = false;
|
|
||||||
detail.type = 'pan';
|
|
||||||
|
|
||||||
if (this.onMove) {
|
|
||||||
this.onMove(detail);
|
|
||||||
} else {
|
|
||||||
Ionic.emit(this, 'ionGestureMove', { detail: this.detail });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (this.pan.detect(detail.currentX, detail.currentY)) {
|
|
||||||
if (this.pan.isGesture() !== 0) {
|
|
||||||
if (!this.tryToCapturePan(ev)) {
|
|
||||||
this.abortGesture();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private calcGestureData(ev: UIEvent) {
|
|
||||||
const detail = this.detail;
|
|
||||||
detail.currentX = pointerCoordX(ev);
|
|
||||||
detail.currentY = pointerCoordY(ev);
|
|
||||||
detail.deltaX = (detail.currentX - detail.startX);
|
|
||||||
detail.deltaY = (detail.currentY - detail.startY);
|
|
||||||
detail.event = ev;
|
|
||||||
|
|
||||||
// figure out which direction we're movin'
|
|
||||||
detail.directionX = detail.velocityDirectionX = (detail.deltaX > 0 ? 'left' : (detail.deltaX < 0 ? 'right' : null));
|
|
||||||
detail.directionY = detail.velocityDirectionY = (detail.deltaY > 0 ? 'up' : (detail.deltaY < 0 ? 'down' : null));
|
|
||||||
|
|
||||||
const positions = this.positions;
|
|
||||||
positions.push(detail.currentX, detail.currentY, detail.timeStamp);
|
|
||||||
|
|
||||||
var endPos = (positions.length - 1);
|
|
||||||
var startPos = endPos;
|
|
||||||
var timeRange = (detail.timeStamp - 100);
|
|
||||||
|
|
||||||
// move pointer to position measured 100ms ago
|
|
||||||
for (var i = endPos; i > 0 && positions[i] > timeRange; i -= 3) {
|
|
||||||
startPos = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startPos !== endPos) {
|
|
||||||
// compute relative movement between these two points
|
|
||||||
var movedX = (positions[startPos - 2] - positions[endPos - 2]);
|
|
||||||
var movedY = (positions[startPos - 1] - positions[endPos - 1]);
|
|
||||||
var factor = 16.67 / (positions[endPos] - positions[startPos]);
|
|
||||||
|
|
||||||
// based on XXms compute the movement to apply for each render step
|
|
||||||
detail.velocityX = movedX * factor;
|
|
||||||
detail.velocityY = movedY * factor;
|
|
||||||
|
|
||||||
detail.velocityDirectionX = (movedX > 0 ? 'left' : (movedX < 0 ? 'right' : null));
|
|
||||||
detail.velocityDirectionY = (movedY > 0 ? 'up' : (movedY < 0 ? 'down' : null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private tryToCapturePan(ev: UIEvent): boolean {
|
|
||||||
if (this.gesture && !this.gesture.capture()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.detail.event = ev;
|
|
||||||
|
|
||||||
if (this.onStart) {
|
|
||||||
this.onStart(this.detail);
|
|
||||||
} else {
|
|
||||||
Ionic.emit(this, 'ionGestureStart', { detail: this.detail });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hasCapturedPan = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private abortGesture() {
|
|
||||||
this.hasStartedPan = false;
|
|
||||||
this.hasCapturedPan = false;
|
|
||||||
|
|
||||||
this.gesture && this.gesture.release();
|
|
||||||
|
|
||||||
this.enable(false);
|
|
||||||
this.notCaptured && this.notCaptured(this.detail);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// END *************************
|
|
||||||
|
|
||||||
@Listen('touchend', { passive: true, enabled: false })
|
|
||||||
onTouchEnd(ev: TouchEvent) {
|
|
||||||
this.lastTouch = this.detail.timeStamp = now(ev);
|
|
||||||
|
|
||||||
this.pointerUp(ev);
|
|
||||||
this.enableTouch(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Listen('document:mouseup', { passive: true, enabled: false })
|
|
||||||
onMouseUp(ev: TouchEvent) {
|
|
||||||
const timeStamp = now(ev);
|
|
||||||
|
|
||||||
if (this.lastTouch === 0 || (this.lastTouch + MOUSE_WAIT < timeStamp)) {
|
|
||||||
this.detail.timeStamp = timeStamp;
|
|
||||||
this.pointerUp(ev);
|
|
||||||
this.enableMouse(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private pointerUp(ev: UIEvent) {
|
|
||||||
const detail = this.detail;
|
|
||||||
|
|
||||||
this.gesture && this.gesture.release();
|
|
||||||
|
|
||||||
detail.event = ev;
|
|
||||||
|
|
||||||
this.calcGestureData(ev);
|
|
||||||
|
|
||||||
if (this.pan) {
|
|
||||||
if (this.hasCapturedPan) {
|
|
||||||
detail.type = 'pan';
|
|
||||||
if (this.onEnd) {
|
|
||||||
this.onEnd(detail);
|
|
||||||
} else {
|
|
||||||
Ionic.emit(this, 'ionGestureEnd', { detail: detail });
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (this.hasPress) {
|
|
||||||
this.detectPress();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (this.notCaptured) {
|
|
||||||
this.notCaptured(detail);
|
|
||||||
} else {
|
|
||||||
Ionic.emit(this, 'ionGestureNotCaptured', { detail: detail });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (this.hasPress) {
|
|
||||||
this.detectPress();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hasCapturedPan = false;
|
|
||||||
this.hasStartedPan = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private detectPress() {
|
|
||||||
const detail = this.detail;
|
|
||||||
|
|
||||||
if (Math.abs(detail.startX - detail.currentX) < 10 && Math.abs(detail.startY - detail.currentY) < 10) {
|
|
||||||
detail.type = 'press';
|
|
||||||
|
|
||||||
if (this.onPress) {
|
|
||||||
this.onPress(detail);
|
|
||||||
} else {
|
|
||||||
Ionic.emit(this, 'ionPress', { detail: detail });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ENABLE LISTENERS *************************
|
|
||||||
|
|
||||||
private enableMouse(shouldEnable: boolean) {
|
|
||||||
if (this.requiresMove) {
|
|
||||||
Ionic.listener.enable(this, 'document:mousemove', shouldEnable);
|
|
||||||
}
|
|
||||||
Ionic.listener.enable(this, 'document:mouseup', shouldEnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private enableTouch(shouldEnable: boolean) {
|
|
||||||
if (this.requiresMove) {
|
|
||||||
Ionic.listener.enable(this, 'touchmove', shouldEnable);
|
|
||||||
}
|
|
||||||
Ionic.listener.enable(this, 'touchend', shouldEnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private enable(shouldEnable: boolean) {
|
|
||||||
this.enableMouse(shouldEnable);
|
|
||||||
this.enableTouch(shouldEnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ionViewDidUnload() {
|
|
||||||
if (this.blocker) {
|
|
||||||
this.blocker.destroy();
|
|
||||||
this.blocker = null;
|
|
||||||
}
|
|
||||||
this.gesture && this.gesture.destroy();
|
|
||||||
this.ctrl = this.gesture = this.pan = this.detail = this.detail.event = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const GESTURE_INLINE_STYLES = {
|
|
||||||
'touch-action': 'none',
|
|
||||||
'user-select': 'none',
|
|
||||||
'-webkit-user-drag': 'none',
|
|
||||||
'-webkit-tap-highlight-color': 'rgba(0,0,0,0)'
|
|
||||||
};
|
|
||||||
|
|
||||||
const MOUSE_WAIT = 2500;
|
|
||||||
|
|
||||||
|
|
||||||
function now(ev: UIEvent) {
|
|
||||||
return ev.timeStamp || Date.now();
|
|
||||||
}
|
|
||||||
|
|
@ -1,361 +0,0 @@
|
|||||||
import { Component, Listen, Prop, Ionic } from '@stencil/core';
|
|
||||||
import { GestureController, GestureDelegate } from '../gesture/gesture-controller';
|
|
||||||
import { GlobalNamespace, ScrollCallback, ScrollDetail } from '../../utils/interfaces';
|
|
||||||
import { Scroll as IScroll } from './scroll-interface';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
tag: 'ion-scroll'
|
|
||||||
})
|
|
||||||
export class Scroll implements IScroll {
|
|
||||||
private $el: HTMLElement;
|
|
||||||
private gesture: GestureDelegate;
|
|
||||||
private positions: number[] = [];
|
|
||||||
private _l: number;
|
|
||||||
private _t: number;
|
|
||||||
private tmr: any;
|
|
||||||
private queued = false;
|
|
||||||
|
|
||||||
isScrolling: boolean = false;
|
|
||||||
detail: ScrollDetail = {};
|
|
||||||
|
|
||||||
@Prop() enabled: boolean = true;
|
|
||||||
@Prop() jsScroll: boolean = false;
|
|
||||||
@Prop() ionScrollStart: ScrollCallback;
|
|
||||||
@Prop() ionScroll: ScrollCallback;
|
|
||||||
@Prop() ionScrollEnd: ScrollCallback;
|
|
||||||
|
|
||||||
|
|
||||||
ionViewDidLoad() {
|
|
||||||
if (Ionic.isServer) return;
|
|
||||||
|
|
||||||
const ctrl = (<GlobalNamespace>Ionic).controllers.gesture = ((<GlobalNamespace>Ionic).controllers.gesture || new GestureController());
|
|
||||||
|
|
||||||
this.gesture = ctrl.createGesture('scroll', 100, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Native Scroll *************************
|
|
||||||
|
|
||||||
@Listen('scroll', { passive: true })
|
|
||||||
onNativeScroll() {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
if (!self.queued && self.enabled) {
|
|
||||||
self.queued = true;
|
|
||||||
|
|
||||||
Ionic.dom.read(function(timeStamp) {
|
|
||||||
self.queued = false;
|
|
||||||
self.onScroll(timeStamp || Date.now());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onScroll(timeStamp: number) {
|
|
||||||
const self = this;
|
|
||||||
const detail = self.detail;
|
|
||||||
const positions = self.positions;
|
|
||||||
|
|
||||||
detail.timeStamp = timeStamp;
|
|
||||||
|
|
||||||
// get the current scrollTop
|
|
||||||
// ******** DOM READ ****************
|
|
||||||
detail.scrollTop = self.getTop();
|
|
||||||
|
|
||||||
// get the current scrollLeft
|
|
||||||
// ******** DOM READ ****************
|
|
||||||
detail.scrollLeft = self.getLeft();
|
|
||||||
|
|
||||||
if (!self.isScrolling) {
|
|
||||||
// currently not scrolling, so this is a scroll start
|
|
||||||
self.isScrolling = true;
|
|
||||||
|
|
||||||
// remember the start positions
|
|
||||||
detail.startY = detail.scrollTop;
|
|
||||||
detail.startX = detail.scrollLeft;
|
|
||||||
|
|
||||||
// new scroll, so do some resets
|
|
||||||
detail.velocityY = detail.velocityX = detail.deltaY = detail.deltaX = positions.length = 0;
|
|
||||||
|
|
||||||
// emit only on the first scroll event
|
|
||||||
if (self.ionScrollStart) {
|
|
||||||
self.ionScrollStart(detail);
|
|
||||||
} else {
|
|
||||||
Ionic.emit(this, 'ionScrollStart', { detail: detail });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
detail.directionX = detail.velocityDirectionX = (detail.deltaX > 0 ? 'left' : (detail.deltaX < 0 ? 'right' : null));
|
|
||||||
detail.directionY = detail.velocityDirectionY = (detail.deltaY > 0 ? 'up' : (detail.deltaY < 0 ? 'down' : null));
|
|
||||||
|
|
||||||
// actively scrolling
|
|
||||||
positions.push(detail.scrollTop, detail.scrollLeft, detail.timeStamp);
|
|
||||||
|
|
||||||
if (positions.length > 3) {
|
|
||||||
// we've gotten at least 2 scroll events so far
|
|
||||||
detail.deltaY = (detail.scrollTop - detail.startY);
|
|
||||||
detail.deltaX = (detail.scrollLeft - detail.startX);
|
|
||||||
|
|
||||||
var endPos = (positions.length - 1);
|
|
||||||
var startPos = endPos;
|
|
||||||
var timeRange = (detail.timeStamp - 100);
|
|
||||||
|
|
||||||
// move pointer to position measured 100ms ago
|
|
||||||
for (var i = endPos; i > 0 && positions[i] > timeRange; i -= 3) {
|
|
||||||
startPos = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startPos !== endPos) {
|
|
||||||
// compute relative movement between these two points
|
|
||||||
var movedTop = (positions[startPos - 2] - positions[endPos - 2]);
|
|
||||||
var movedLeft = (positions[startPos - 1] - positions[endPos - 1]);
|
|
||||||
var factor = 16.67 / (positions[endPos] - positions[startPos]);
|
|
||||||
|
|
||||||
// based on XXms compute the movement to apply for each render step
|
|
||||||
detail.velocityY = movedTop * factor;
|
|
||||||
detail.velocityX = movedLeft * factor;
|
|
||||||
|
|
||||||
// figure out which direction we're scrolling
|
|
||||||
detail.velocityDirectionX = (movedLeft > 0 ? 'left' : (movedLeft < 0 ? 'right' : null));
|
|
||||||
detail.velocityDirectionY = (movedTop > 0 ? 'up' : (movedTop < 0 ? 'down' : null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearTimeout(self.tmr);
|
|
||||||
self.tmr = setTimeout(function() {
|
|
||||||
|
|
||||||
// haven't scrolled in a while, so it's a scrollend
|
|
||||||
self.isScrolling = false;
|
|
||||||
|
|
||||||
Ionic.dom.read(function(timeStamp) {
|
|
||||||
if (!self.isScrolling) {
|
|
||||||
self.onEnd(timeStamp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 80);
|
|
||||||
|
|
||||||
// emit on each scroll event
|
|
||||||
if (self.ionScrollStart) {
|
|
||||||
self.ionScroll(detail);
|
|
||||||
} else {
|
|
||||||
Ionic.emit(this, 'ionScroll', { detail: detail });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onEnd(timeStamp: number) {
|
|
||||||
const self = this;
|
|
||||||
const detail = self.detail;
|
|
||||||
|
|
||||||
detail.timeStamp = timeStamp || Date.now();
|
|
||||||
|
|
||||||
// emit that the scroll has ended
|
|
||||||
if (self.ionScrollEnd) {
|
|
||||||
self.ionScrollEnd(detail);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Ionic.emit(this, 'ionScrollEnd', { detail: detail });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enableJsScroll(contentTop: number, contentBottom: number) {
|
|
||||||
this.jsScroll = true;
|
|
||||||
|
|
||||||
Ionic.listener.enable(this, 'scroll', false);
|
|
||||||
|
|
||||||
Ionic.listener.enable(this, 'touchstart', true);
|
|
||||||
|
|
||||||
contentTop; contentBottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Touch Scroll *************************
|
|
||||||
|
|
||||||
@Listen('touchstart', { passive: true, enabled: false })
|
|
||||||
onTouchStart() {
|
|
||||||
if (!this.enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ionic.listener.enable(this, 'touchmove', true);
|
|
||||||
Ionic.listener.enable(this, 'touchend', true);
|
|
||||||
|
|
||||||
throw 'jsScroll: TODO!';
|
|
||||||
}
|
|
||||||
|
|
||||||
@Listen('touchmove', { passive: true, enabled: false })
|
|
||||||
onTouchMove() {
|
|
||||||
if (!this.enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Listen('touchend', { passive: true, enabled: false })
|
|
||||||
onTouchEnd() {
|
|
||||||
Ionic.listener.enable(this, 'touchmove', false);
|
|
||||||
Ionic.listener.enable(this, 'touchend', false);
|
|
||||||
|
|
||||||
if (!this.enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DOM READ
|
|
||||||
*/
|
|
||||||
getTop() {
|
|
||||||
if (this.jsScroll) {
|
|
||||||
return this._t;
|
|
||||||
}
|
|
||||||
return this._t = this.$el.scrollTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DOM READ
|
|
||||||
*/
|
|
||||||
getLeft() {
|
|
||||||
if (this.jsScroll) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return this._l = this.$el.scrollLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DOM WRITE
|
|
||||||
*/
|
|
||||||
setTop(top: number) {
|
|
||||||
this._t = top;
|
|
||||||
|
|
||||||
if (this.jsScroll) {
|
|
||||||
this.$el.style.transform = this.$el.style.webkitTransform = `translate3d(${this._l * -1}px,${top * -1}px,0px)`;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.$el.scrollTop = top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DOM WRITE
|
|
||||||
*/
|
|
||||||
setLeft(left: number) {
|
|
||||||
this._l = left;
|
|
||||||
|
|
||||||
if (this.jsScroll) {
|
|
||||||
this.$el.style.transform = this.$el.style.webkitTransform = `translate3d(${left * -1}px,${this._t * -1}px,0px)`;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.$el.scrollLeft = left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollTo(x: number, y: number, duration: number, done?: Function): Promise<any> {
|
|
||||||
// scroll animation loop w/ easing
|
|
||||||
// credit https://gist.github.com/dezinezync/5487119
|
|
||||||
|
|
||||||
let promise: Promise<any>;
|
|
||||||
if (done === undefined) {
|
|
||||||
// only create a promise if a done callback wasn't provided
|
|
||||||
// done can be a null, which avoids any functions
|
|
||||||
promise = new Promise(resolve => {
|
|
||||||
done = resolve;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const self = this;
|
|
||||||
const el = self.$el;
|
|
||||||
if (!el) {
|
|
||||||
// invalid element
|
|
||||||
done();
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (duration < 32) {
|
|
||||||
self.setTop(y);
|
|
||||||
self.setLeft(x);
|
|
||||||
done();
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fromY = el.scrollTop;
|
|
||||||
const fromX = el.scrollLeft;
|
|
||||||
|
|
||||||
const maxAttempts = (duration / 16) + 100;
|
|
||||||
|
|
||||||
let startTime: number;
|
|
||||||
let attempts = 0;
|
|
||||||
let stopScroll = false;
|
|
||||||
|
|
||||||
// scroll loop
|
|
||||||
function step(timeStamp: number) {
|
|
||||||
attempts++;
|
|
||||||
|
|
||||||
if (!self.$el || stopScroll || attempts > maxAttempts) {
|
|
||||||
self.isScrolling = false;
|
|
||||||
el.style.transform = el.style.webkitTransform = '';
|
|
||||||
done();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let time = Math.min(1, ((timeStamp - startTime) / duration));
|
|
||||||
|
|
||||||
// where .5 would be 50% of time on a linear scale easedT gives a
|
|
||||||
// fraction based on the easing method
|
|
||||||
let easedT = (--time) * time * time + 1;
|
|
||||||
|
|
||||||
if (fromY !== y) {
|
|
||||||
self.setTop((easedT * (y - fromY)) + fromY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fromX !== x) {
|
|
||||||
self.setLeft(Math.floor((easedT * (x - fromX)) + fromX));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (easedT < 1) {
|
|
||||||
// do not use DomController here
|
|
||||||
// must use nativeRaf in order to fire in the next frame
|
|
||||||
Ionic.dom.raf(step);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
stopScroll = true;
|
|
||||||
self.isScrolling = false;
|
|
||||||
el.style.transform = el.style.webkitTransform = '';
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// start scroll loop
|
|
||||||
self.isScrolling = true;
|
|
||||||
|
|
||||||
// chill out for a frame first
|
|
||||||
Ionic.dom.write(() => {
|
|
||||||
Ionic.dom.write(timeStamp => {
|
|
||||||
startTime = timeStamp;
|
|
||||||
step(timeStamp);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToTop(duration: number): Promise<void> {
|
|
||||||
return this.scrollTo(0, 0, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollToBottom(duration: number): Promise<void> {
|
|
||||||
let y = 0;
|
|
||||||
if (this.$el) {
|
|
||||||
y = this.$el.scrollHeight - this.$el.clientHeight;
|
|
||||||
}
|
|
||||||
return this.scrollTo(0, y, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ionViewDidUnload() {
|
|
||||||
this.gesture && this.gesture.destroy();
|
|
||||||
this.gesture = this.detail = this.detail.event = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Reference in New Issue
Block a user