Преглед изворни кода

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.
Tusooa Zhu пре 3 година
родитељ
комит
f96e5882d1

+ 11 - 14
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')

+ 1 - 0
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"
     >

+ 81 - 2
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