浏览代码

Handle pinch action

Tusooa Zhu 3 年之前
父节点
当前提交
d9030b4fdd

+ 10 - 1
src/components/media_modal/media_modal.js

@@ -61,7 +61,7 @@ const MediaModal = {
       return this.$store.state.mediaViewer.swipeScaler.offsets
     },
     transform () {
-      return `scale(${this.scaling}, ${this.scaling}) translate(${this.offsets[0]}px, ${this.offsets[1]}px)`
+      return `translate(${this.offsets[0]}px, ${this.offsets[1]}px) scale(${this.scaling}, ${this.scaling})`
     }
   },
   created () {
@@ -71,6 +71,8 @@ const MediaModal = {
       callbackNegative: this.goPrev,
       swipePreviewCallback: this.handleSwipePreview,
       swipeEndCallback: this.handleSwipeEnd,
+      pinchPreviewCallback: this.handlePinchPreview,
+      pinchEndCallback: this.handlePinchEnd,
       threshold: 50
     })
   },
@@ -125,6 +127,13 @@ const MediaModal = {
         this.goPrev()
       }
     },
+    handlePinchPreview (offsets, scaling) {
+      console.log('handle pinch preview:', offsets, scaling)
+      this.$store.dispatch('swipeScaler/apply', { offsets, scaling })
+    },
+    handlePinchEnd () {
+      this.$store.dispatch('swipeScaler/finish')
+    },
     handleKeyupEvent (e) {
       if (this.showing && e.keyCode === 27) { // escape
         this.hide()

+ 3 - 3
src/components/media_modal/media_modal.vue

@@ -12,9 +12,9 @@
       :alt="currentMedia.description"
       :title="currentMedia.description"
       :style="{ transform }"
-      @touchstart.stop="mediaTouchStart"
-      @touchmove.stop="mediaTouchMove"
-      @touchend.stop="mediaTouchEnd"
+      @touchstart.stop.prevent="mediaTouchStart"
+      @touchmove.stop.prevent="mediaTouchMove"
+      @touchend.stop.prevent="mediaTouchEnd"
       @click="hide"
       @load="onImageLoaded"
     >

+ 4 - 10
src/modules/media_viewer.js

@@ -62,16 +62,12 @@ const mediaViewer = {
         applyScaling (state, { scaling }) {
           state.scaling = state.origScaling * scaling
         },
-        finishOffsets (state) {
+        finish (state) {
           state.origOffsets = [...state.offsets]
-        },
-        finishScaling (state) {
           state.origScaling = state.scaling
         },
-        revertOffsets (state) {
+        revert (state) {
           state.offsets = [...state.origOffsets]
-        },
-        revertScaling (state) {
           state.scaling = state.origScaling
         }
       },
@@ -85,12 +81,10 @@ const mediaViewer = {
           commit('applyScaling', { scaling })
         },
         finish ({ commit }) {
-          commit('finishOffsets')
-          commit('finishScaling')
+          commit('finish')
         },
         revert ({ commit }) {
-          commit('revertOffsets')
-          commit('revertScaling')
+          commit('revert')
         }
       }
     }

+ 40 - 2
src/services/gesture_service/gesture_service.js

@@ -4,14 +4,21 @@ const DIRECTION_RIGHT = [1, 0]
 const DIRECTION_UP = [0, -1]
 const DIRECTION_DOWN = [0, 1]
 
+const DISTANCE_MIN = 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 isScaleEventEnd = e => (e.targetTouches.length === 1)
 
 const deltaCoord = (oldCoord, newCoord) => [newCoord[0] - oldCoord[0], newCoord[1] - oldCoord[1]]
 
+const vectorMinus = (a, b) => a.map((k, n) => k - b[n])
+const vectorAdd = (a, b) => a.map((k, n) => k + b[n])
+
+const avgCoord = (coords) => [...coords].reduce(vectorAdd, [0, 0]).map(d => d / coords.length)
+
 const touchCoord = touch => [touch.screenX, touch.screenY]
 
 const touchEventCoord = e => touchCoord(e.touches[0])
@@ -22,6 +29,8 @@ const perpendicular = v => [v[1], -v[0]]
 
 const dotProduct = (v1, v2) => v1[0] * v2[0] + v1[1] * v2[1]
 
+// const numProduct = (num, v) => v.map(k => num * k)
+
 const project = (v1, v2) => {
   const scalar = (dotProduct(v1, v2) / dotProduct(v2, v2))
   return [scalar * v2[0], scalar * v2[1]]
@@ -86,7 +95,7 @@ class SwipeAndScaleGesture {
     swipeEndCallback, pinchEndCallback,
     threshold = 30, perpendicularTolerance = 1.0
   }) {
-    const nop = () => {}
+    const nop = () => { console.log('Warning: Not implemented') }
     this.direction = direction
     this.swipePreviewCallback = swipePreviewCallback || nop
     this.pinchPreviewCallback = pinchPreviewCallback || nop
@@ -95,6 +104,7 @@ class SwipeAndScaleGesture {
     this.threshold = threshold
     this.perpendicularTolerance = perpendicularTolerance
     this._startPos = [0, 0]
+    this._startDistance = DISTANCE_MIN
     this._swiping = false
   }
 
@@ -105,23 +115,51 @@ class SwipeAndScaleGesture {
       console.log('start pos:', this._startPos)
       this._swiping = true
     } else if (isScaleEvent(event)) {
+      const coords = [...event.targetTouches].map(touchCoord)
+      this._startPos = avgCoord(coords)
+      this._startDistance = vectorLength(deltaCoord(coords[0], coords[1]))
+      if (this._startDistance < DISTANCE_MIN) {
+        this._startDistance = DISTANCE_MIN
+      }
       this._scalePoints = [...event.targetTouches]
       this._swiping = false
+      console.log(
+        'is scale event, start =', this._startPos,
+        'dist =', this._startDistance)
     }
   }
 
   move (event) {
+    // console.log('move called', event)
     if (isSwipeEvent(event)) {
       const touch = event.changedTouches[0]
       const delta = deltaCoord(this._startPos, touchCoord(touch))
 
       this.swipePreviewCallback(delta)
     } else if (isScaleEvent(event)) {
+      console.log('is scale event')
+      const coords = [...event.targetTouches].map(touchCoord)
+      const curPos = avgCoord(coords)
+      const curDistance = vectorLength(deltaCoord(coords[0], coords[1]))
+      const scaling = curDistance / this._startDistance
+      const posDiff = vectorMinus(curPos, this._startPos)
+      // const delta = vectorAdd(numProduct((1 - scaling), this._startPos), posDiff)
+      const delta = posDiff
+      // console.log(
+      //   'is scale event, cur =', curPos,
+      //   'dist =', curDistance,
+      //   'scale =', scaling,
+      //   'delta =', delta)
+      this.pinchPreviewCallback(delta, scaling)
     }
   }
 
   end (event) {
     console.log('end() called', event)
+    if (isScaleEventEnd(event)) {
+      this.pinchEndCallback()
+    }
+
     if (!isSwipeEventEnd(event)) {
       console.log('not swipe event')
       return