mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 01:47:22 +08:00
FEATURE: add swipe detection for dismissing mobile topic scroller
This commit is contained in:
parent
c33ee13c4c
commit
8bdb62ca01
|
@ -1,9 +1,11 @@
|
|||
import { observes } from "ember-addons/ember-computed-decorators";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import PanEvents from "discourse/mixins/pan-events";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
export default Ember.Component.extend(PanEvents, {
|
||||
composerOpen: null,
|
||||
info: null,
|
||||
isPanning: false,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
@ -91,7 +93,7 @@ export default Ember.Component.extend({
|
|||
_collapseFullscreen() {
|
||||
if (this.get("info.topicProgressExpanded")) {
|
||||
$(".timeline-fullscreen").removeClass("show");
|
||||
setTimeout(() => {
|
||||
Ember.run.later(() => {
|
||||
this.set("info.topicProgressExpanded", false);
|
||||
this._checkSize();
|
||||
}, 500);
|
||||
|
@ -109,6 +111,59 @@ export default Ember.Component.extend({
|
|||
}
|
||||
},
|
||||
|
||||
_panOpenClose(offset, velocity, direction) {
|
||||
|
||||
const $timelineContainer = $(".timeline-container");
|
||||
const maxOffset = parseInt($timelineContainer.css("height"));
|
||||
direction === "close" ? offset += velocity : offset -= velocity;
|
||||
|
||||
$timelineContainer.css("bottom", -offset);
|
||||
if(offset > maxOffset) {
|
||||
this._collapseFullscreen();
|
||||
}
|
||||
else if(offset <= 0) {
|
||||
$timelineContainer.css("bottom", "");
|
||||
}
|
||||
else {
|
||||
Ember.run.later(() => this._panOpenClose(offset, velocity, direction), 20);
|
||||
}
|
||||
},
|
||||
|
||||
_shouldPanClose(e) {
|
||||
return (e.deltaY > 200 && e.velocityY > -0.15) || e.velocityY > 0.15;
|
||||
},
|
||||
|
||||
panStart(e) {
|
||||
const center = e.center;
|
||||
const $centeredElement = $(document.elementFromPoint(center.x, center.y));
|
||||
if ($centeredElement.parents(".timeline-scrollarea-wrapper").length) {
|
||||
this.set("isPanning", false);
|
||||
}
|
||||
else {
|
||||
this.set("isPanning", true);
|
||||
}
|
||||
},
|
||||
|
||||
panEnd(e) {
|
||||
if(!this.get("isPanning")) {
|
||||
return;
|
||||
}
|
||||
this.set("isPanning", false);
|
||||
if(this._shouldPanClose(e)) {
|
||||
this._panOpenClose(e.deltaY, 40, "close");
|
||||
}
|
||||
else {
|
||||
this._panOpenClose(e.deltaY, 40, "open");
|
||||
}
|
||||
},
|
||||
|
||||
panMove(e) {
|
||||
if(!this.get("isPanning")) {
|
||||
return;
|
||||
}
|
||||
$(".timeline-container").css("bottom", Math.min(0, -e.deltaY));
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
|
||||
|
|
89
app/assets/javascripts/discourse/mixins/pan-events.js.es6
Normal file
89
app/assets/javascripts/discourse/mixins/pan-events.js.es6
Normal file
|
@ -0,0 +1,89 @@
|
|||
import {
|
||||
on,
|
||||
default as computed
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
|
||||
//velocity is pixels per ms
|
||||
|
||||
_panState: null,
|
||||
|
||||
didInsertElement() {
|
||||
this.$().on("pointerdown", (e) => this._panStart(e))
|
||||
.on("pointermove", (e) => this._panMove(e))
|
||||
.on("pointerup", (e) => this._panMove(e))
|
||||
.on("pointercancel", (e) => this._panMove(e));
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this.$().off("pointerdown")
|
||||
.off("pointerup")
|
||||
.off("pointermove")
|
||||
.off("pointercancel");
|
||||
},
|
||||
|
||||
_calculateNewPanState(oldState, e) {
|
||||
if(e.type == "pointerup" || e.type == "pointercancel") {
|
||||
return oldState;
|
||||
}
|
||||
const newTimestamp = new Date().getTime();
|
||||
const timeDiffSeconds = (newTimestamp - oldState.timestamp);
|
||||
//calculate delta x, y, distance from START location
|
||||
const deltaX = Math.round(e.clientX) - oldState.startLocation.x;
|
||||
const deltaY = Math.round(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
|
||||
};
|
||||
},
|
||||
|
||||
_panStart(e) {
|
||||
const newState = {
|
||||
center: {x: Math.round(e.clientX), y: Math.round(e.clientY)},
|
||||
startLocation: {x: Math.round(e.clientX), y: Math.round(e.clientY)},
|
||||
velocity: 0,
|
||||
velocityX: 0,
|
||||
velocityY: 0,
|
||||
deltaX: 0,
|
||||
deltaY: 0,
|
||||
distance: 0,
|
||||
start: true,
|
||||
timestamp: new Date().getTime()
|
||||
};
|
||||
this.set("_panState", newState);
|
||||
},
|
||||
|
||||
_panMove(e) {
|
||||
const previousState = this.get("_panState");
|
||||
const newState = this._calculateNewPanState(previousState, e);
|
||||
this.set("_panState", newState);
|
||||
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);
|
||||
}
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue
Block a user