commit: f96e5882d16a8662ceb7b8cc6aa36fe131a2682f
parent 51b14cc61578c6aee80fbf63dce2dbb7503914bb
Author: Tusooa Zhu <tusooa@kazv.moe>
Date: Sun, 1 Aug 2021 13:39:56 -0400
Make media modal be aware of multi-touch actions
Originally the media viewer would think every touch is a swipe (one-finger
touch event), so we would encounter the case where a two-finger scale event
would incorrectly change the current media. This is now fixed.
Diffstat:
3 files changed, 93 insertions(+), 16 deletions(-)
diff --git a/src/components/media_modal/media_modal.js b/src/components/media_modal/media_modal.js
@@ -53,28 +53,25 @@ const MediaModal = {
}
},
created () {
- this.mediaSwipeGestureRight = GestureService.swipeGesture(
- GestureService.DIRECTION_RIGHT,
- this.goPrev,
- 50
- )
- this.mediaSwipeGestureLeft = GestureService.swipeGesture(
- GestureService.DIRECTION_LEFT,
- this.goNext,
- 50
- )
+ this.mediaGesture = new GestureService.SwipeAndScaleGesture({
+ direction: GestureService.DIRECTION_LEFT,
+ callbackPositive: this.goNext,
+ callbackNegative: this.goPrev,
+ threshold: 50
+ })
},
methods: {
getType (media) {
return fileTypeService.fileType(media.mimetype)
},
mediaTouchStart (e) {
- GestureService.beginSwipe(e, this.mediaSwipeGestureRight)
- GestureService.beginSwipe(e, this.mediaSwipeGestureLeft)
+ this.mediaGesture.start(e)
},
mediaTouchMove (e) {
- GestureService.updateSwipe(e, this.mediaSwipeGestureRight)
- GestureService.updateSwipe(e, this.mediaSwipeGestureLeft)
+ this.mediaGesture.move(e)
+ },
+ mediaTouchEnd (e) {
+ this.mediaGesture.end(e)
},
hide () {
this.$store.dispatch('closeMediaViewer')
diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue
@@ -13,6 +13,7 @@
:title="currentMedia.description"
@touchstart.stop="mediaTouchStart"
@touchmove.stop="mediaTouchMove"
+ @touchend.stop="mediaTouchEnd"
@click="hide"
@load="onImageLoaded"
>
diff --git a/src/services/gesture_service/gesture_service.js b/src/services/gesture_service/gesture_service.js
@@ -4,9 +4,17 @@ const DIRECTION_RIGHT = [1, 0]
const DIRECTION_UP = [0, -1]
const DIRECTION_DOWN = [0, 1]
+const isSwipeEvent = e => (e.touches.length === 1)
+const isSwipeEventEnd = e => (e.changedTouches.length === 1)
+
+const isScaleEvent = e => (e.targetTouches.length === 2)
+// const isScaleEventEnd = e => (e.changedTouches.length === 2)
+
const deltaCoord = (oldCoord, newCoord) => [newCoord[0] - oldCoord[0], newCoord[1] - oldCoord[1]]
-const touchEventCoord = e => ([e.touches[0].screenX, e.touches[0].screenY])
+const touchCoord = touch => [touch.screenX, touch.screenY]
+
+const touchEventCoord = e => touchCoord(e.touches[0])
const vectorLength = v => Math.sqrt(v[0] * v[0] + v[1] * v[1])
@@ -61,6 +69,76 @@ const updateSwipe = (event, gesture) => {
gesture._swiping = false
}
+class SwipeAndScaleGesture {
+ constructor ({
+ direction, callbackPositive, callbackNegative,
+ previewCallback, threshold = 30, perpendicularTolerance = 1.0
+ }) {
+ this.direction = direction
+ this.previewCallback = previewCallback
+ this.callbackPositive = callbackPositive
+ this.callbackNegative = callbackNegative
+ this.threshold = threshold
+ this.perpendicularTolerance = perpendicularTolerance
+ this._startPos = [0, 0]
+ this._swiping = false
+ }
+
+ start (event) {
+ console.log('start() called', event)
+ if (isSwipeEvent(event)) {
+ this._startPos = touchEventCoord(event)
+ console.log('start pos:', this._startPos)
+ this._swiping = true
+ } else if (isScaleEvent(event)) {
+ this._scalePoints = [...event.targetTouches]
+ this._swiping = false
+ }
+ }
+
+ move (event) {
+ if (isScaleEvent(event)) {
+ }
+ }
+
+ end (event) {
+ console.log('end() called', event)
+ if (!isSwipeEventEnd(event)) {
+ console.log('not swipe event')
+ return
+ }
+ if (!this._swiping) {
+ console.log('not swiping')
+ return
+ }
+ this.swiping = false
+
+ console.log('is swipe event')
+
+ // movement too small
+ const touch = event.changedTouches[0]
+ const delta = deltaCoord(this._startPos, touchCoord(touch))
+ if (vectorLength(delta) < this.threshold) return
+ // movement is opposite from direction
+ const isPositive = dotProduct(delta, this.direction) > 0
+
+ // movement perpendicular to direction is too much
+ const towardsDir = project(delta, this.direction)
+ const perpendicularDir = perpendicular(this.direction)
+ const towardsPerpendicular = project(delta, perpendicularDir)
+ if (
+ vectorLength(towardsDir) * this.perpendicularTolerance <
+ vectorLength(towardsPerpendicular)
+ ) return
+
+ if (isPositive) {
+ this.callbackPositive()
+ } else {
+ this.callbackNegative()
+ }
+ }
+}
+
const GestureService = {
DIRECTION_LEFT,
DIRECTION_RIGHT,
@@ -68,7 +146,8 @@ const GestureService = {
DIRECTION_DOWN,
swipeGesture,
beginSwipe,
- updateSwipe
+ updateSwipe,
+ SwipeAndScaleGesture
}
export default GestureService