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.
This commit is contained in:
Tusooa Zhu 2021-08-01 13:39:56 -04:00
parent 51b14cc615
commit f96e5882d1
No known key found for this signature in database
GPG key ID: 7B467EDE43A08224
3 changed files with 93 additions and 16 deletions

View file

@ -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')

View file

@ -13,6 +13,7 @@
:title="currentMedia.description"
@touchstart.stop="mediaTouchStart"
@touchmove.stop="mediaTouchMove"
@touchend.stop="mediaTouchEnd"
@click="hide"
@load="onImageLoaded"
>

View file

@ -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