Tusooa Zhu 3 жил өмнө
parent
commit
a7570f5eb2

+ 26 - 0
src/components/media_modal/media_modal.js

@@ -4,6 +4,7 @@ import Modal from '../modal/modal.vue'
 import fileTypeService from '../../services/file_type/file_type.service.js'
 import GestureService from '../../services/gesture_service/gesture_service'
 import Flash from 'src/components/flash/flash.vue'
+import Vuex from 'vuex'
 import { library } from '@fortawesome/fontawesome-svg-core'
 import {
   faChevronLeft,
@@ -17,6 +18,8 @@ library.add(
   faCircleNotch
 )
 
+const onlyXAxis = ([x, y]) => [x, 0]
+
 const MediaModal = {
   components: {
     StillImage,
@@ -50,6 +53,15 @@ const MediaModal = {
     },
     type () {
       return this.currentMedia ? this.getType(this.currentMedia) : null
+    },
+    scaling () {
+      return this.$store.state.mediaViewer.swipeScaler.scaling
+    },
+    offsets () {
+      return this.$store.state.mediaViewer.swipeScaler.offsets
+    },
+    transform () {
+      return `scale(${this.scaling}, ${this.scaling}) translate(${this.offsets[0]}px, ${this.offsets[1]}px)`
     }
   },
   created () {
@@ -57,6 +69,8 @@ const MediaModal = {
       direction: GestureService.DIRECTION_LEFT,
       callbackPositive: this.goNext,
       callbackNegative: this.goPrev,
+      swipePreviewCallback: this.handleSwipePreview,
+      swipeEndCallback: this.handleSwipeEnd,
       threshold: 50
     })
   },
@@ -99,6 +113,18 @@ const MediaModal = {
     onImageLoaded () {
       this.loading = false
     },
+    handleSwipePreview (offsets) {
+      this.$store.dispatch('swipeScaler/apply', { offsets: onlyXAxis(offsets) })
+    },
+    handleSwipeEnd (sign) {
+      if (sign === 0) {
+        this.$store.dispatch('swipeScaler/revert')
+      } else if (sign > 0) {
+        this.goNext()
+      } else {
+        this.goPrev()
+      }
+    },
     handleKeyupEvent (e) {
       if (this.showing && e.keyCode === 27) { // escape
         this.hide()

+ 1 - 0
src/components/media_modal/media_modal.vue

@@ -11,6 +11,7 @@
       :src="currentMedia.url"
       :alt="currentMedia.description"
       :title="currentMedia.description"
+      :style="{ transform }"
       @touchstart.stop="mediaTouchStart"
       @touchmove.stop="mediaTouchMove"
       @touchend.stop="mediaTouchEnd"

+ 62 - 2
src/modules/media_viewer.js

@@ -20,19 +20,79 @@ const mediaViewer = {
     }
   },
   actions: {
-    setMedia ({ commit }, attachments) {
+    setMedia ({ commit, dispatch }, attachments) {
       const media = attachments.filter(attachment => {
         const type = fileTypeService.fileType(attachment.mimetype)
         return supportedTypes.has(type)
       })
       commit('setMedia', media)
+      dispatch('swipeScaler/reset')
     },
     setCurrentMedia ({ commit, state }, current) {
       const index = state.media.indexOf(current)
       commit('setCurrentMedia', index || 0)
+      dispatch('swipeScaler/reset')
     },
-    closeMediaViewer ({ commit }) {
+    closeMediaViewer ({ commit, dispatch }) {
       commit('close')
+      dispatch('swipeScaler/reset')
+    }
+  },
+  modules: {
+    swipeScaler: {
+      namespaced: true,
+
+      state: {
+        origOffsets: [0, 0],
+        offsets: [0, 0],
+        origScaling: 1,
+        scaling: 1
+      },
+
+      mutations: {
+        reset (state) {
+          state.origOffsets = [0, 0]
+          state.offsets = [0, 0]
+          state.origScaling = 1
+          state.scaling = 1
+        },
+        applyOffsets (state, { offsets }) {
+          state.offsets = state.origOffsets.map((k, n) => k + offsets[n])
+        },
+        applyScaling (state, { scaling }) {
+          state.scaling = state.origScaling * scaling
+        },
+        finishOffsets (state) {
+          state.origOffsets = [...state.offsets]
+        },
+        finishScaling (state) {
+          state.origScaling = state.scaling
+        },
+        revertOffsets (state) {
+          state.offsets = [...state.origOffsets]
+        },
+        revertScaling (state) {
+          state.scaling = state.origScaling
+        }
+      },
+
+      actions: {
+        reset ({ commit }) {
+          commit('reset')
+        },
+        apply ({ commit }, { offsets, scaling = 1 }) {
+          commit('applyOffsets', { offsets })
+          commit('applyScaling', { scaling })
+        },
+        finish ({ commit }) {
+          commit('finishOffsets')
+          commit('finishScaling')
+        },
+        revert ({ commit }) {
+          commit('revertOffsets')
+          commit('revertScaling')
+        }
+      }
     }
   }
 }

+ 49 - 24
src/services/gesture_service/gesture_service.js

@@ -70,14 +70,28 @@ const updateSwipe = (event, gesture) => {
 }
 
 class SwipeAndScaleGesture {
+  // swipePreviewCallback(offsets: Array[Number])
+  //   offsets: the offset vector which the underlying component should move, from the starting position
+  // pinchPreviewCallback(offsets: Array[Number], scaling: Number)
+  //   offsets: the offset vector which the underlying component should move, from the starting position
+  //   scaling: the scaling factor we should apply to the underlying component, from the starting position
+  // swipeEndcallback(sign: 0|-1|1)
+  //   sign: if the swipe does not meet the threshold, 0
+  //         if the swipe meets the threshold in the positive direction, 1
+  //         if the swipe meets the threshold in the negative direction, -1
   constructor ({
-    direction, callbackPositive, callbackNegative,
-    previewCallback, threshold = 30, perpendicularTolerance = 1.0
+    direction,
+    // swipeStartCallback, pinchStartCallback,
+    swipePreviewCallback, pinchPreviewCallback,
+    swipeEndCallback, pinchEndCallback,
+    threshold = 30, perpendicularTolerance = 1.0
   }) {
+    const nop = () => {}
     this.direction = direction
-    this.previewCallback = previewCallback
-    this.callbackPositive = callbackPositive
-    this.callbackNegative = callbackNegative
+    this.swipePreviewCallback = swipePreviewCallback || nop
+    this.pinchPreviewCallback = pinchPreviewCallback || nop
+    this.swipeEndCallback = swipeEndCallback || nop
+    this.pinchEndCallback = pinchEndCallback || nop
     this.threshold = threshold
     this.perpendicularTolerance = perpendicularTolerance
     this._startPos = [0, 0]
@@ -97,7 +111,12 @@ class SwipeAndScaleGesture {
   }
 
   move (event) {
-    if (isScaleEvent(event)) {
+    if (isSwipeEvent(event)) {
+      const touch = event.changedTouches[0]
+      const delta = deltaCoord(this._startPos, touchCoord(touch))
+
+      this.swipePreviewCallback(delta)
+    } else if (isScaleEvent(event)) {
     }
   }
 
@@ -118,24 +137,30 @@ class SwipeAndScaleGesture {
     // 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()
-    }
+    this.swipePreviewCallback(delta)
+
+    const sign = (() => {
+      if (vectorLength(delta) < this.threshold) {
+        return 0
+      }
+      // 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 0
+      }
+
+      return isPositive ? 1 : -1
+    })()
+
+    this.swipeEndCallback(sign)
   }
 }