This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/app/assets/javascripts/discourse/mixins/pan-events.js.es6
2019-05-27 10:15:39 +02:00

141 lines
4.0 KiB
JavaScript

/**
Pan events is a mixin that allows components to detect and respond to swipe gestures
It fires callbacks for panStart, panEnd, panMove with the pan state, and the original event.
**/
export const SWIPE_VELOCITY = 40;
export const SWIPE_DISTANCE_THRESHOLD = 50;
export const SWIPE_VELOCITY_THRESHOLD = 0.12;
export const MINIMUM_SWIPE_DISTANCE = 5;
export default Ember.Mixin.create({
//velocity is pixels per ms
_panState: null,
didInsertElement() {
this._super(...arguments);
this.addTouchListeners(this.$());
},
willDestroyElement() {
this._super(...arguments);
this.removeTouchListeners(this.$());
},
addTouchListeners($element) {
if (this.site.mobileView) {
$element
.on("touchstart", e => e.touches && this._panStart(e.touches[0]))
.on("touchmove", e => {
const touchEvent = e.touches[0];
touchEvent.type = "pointermove";
this._panMove(touchEvent, e);
})
.on("touchend", e => this._panMove({ type: "pointerup" }, e))
.on("touchcancel", e => this._panMove({ type: "pointercancel" }, e));
}
},
removeTouchListeners($element) {
if (this.site.mobileView) {
$element
.off("touchstart")
.off("touchmove")
.off("touchend")
.off("touchcancel");
}
},
_calculateDirection(oldState, deltaX, deltaY) {
if (oldState.start || !oldState.direction) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
return deltaX > 0 ? "right" : "left";
} else {
return deltaY > 0 ? "down" : "up";
}
}
return oldState.direction;
},
_calculateNewPanState(oldState, e) {
if (e.type === "pointerup" || e.type === "pointercancel") {
return oldState;
}
const newTimestamp = new Date().getTime();
const timeDiffSeconds = newTimestamp - oldState.timestamp;
if (timeDiffSeconds === 0) {
return oldState;
}
//calculate delta x, y, distance from START location
const deltaX = e.clientX - oldState.startLocation.x;
const deltaY = e.clientY - oldState.startLocation.y;
const distance = Math.round(
Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2))
);
//calculate velocity from previous event center location
const eventDeltaX = e.clientX - oldState.center.x;
const eventDeltaY = e.clientY - oldState.center.y;
const velocityX = eventDeltaX / timeDiffSeconds;
const velocityY = eventDeltaY / timeDiffSeconds;
const deltaDistance = Math.sqrt(
Math.pow(eventDeltaX, 2) + Math.pow(eventDeltaY, 2)
);
const velocity = deltaDistance / timeDiffSeconds;
return {
startLocation: oldState.startLocation,
center: { x: Math.round(e.clientX), y: Math.round(e.clientY) },
velocity,
velocityX,
velocityY,
deltaX,
deltaY,
distance,
start: false,
timestamp: newTimestamp,
direction: this._calculateDirection(oldState, deltaX, deltaY)
};
},
_panStart(e) {
const newState = {
center: { x: Math.round(e.clientX), y: Math.round(e.clientY) },
startLocation: { x: e.clientX, y: e.clientY },
velocity: 0,
velocityX: 0,
velocityY: 0,
deltaX: 0,
deltaY: 0,
distance: 0,
start: true,
timestamp: new Date().getTime(),
direction: null
};
this.set("_panState", newState);
},
_panMove(e, originalEvent) {
if (!this._panState) {
this._panStart(e);
return;
}
const previousState = this._panState;
const newState = this._calculateNewPanState(previousState, e);
if (previousState.start && newState.distance < MINIMUM_SWIPE_DISTANCE) {
return;
}
this.set("_panState", newState);
newState.originalEvent = originalEvent;
if (previousState.start && "panStart" in this) {
this.panStart(newState);
} else if (
(e.type === "pointerup" || e.type === "pointercancel") &&
"panEnd" in this
) {
this.panEnd(newState);
} else if (e.type === "pointermove" && "panMove" in this) {
this.panMove(newState);
}
}
});