forked from AkkomaGang/akkoma-fe
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:
parent
51b14cc615
commit
f96e5882d1
3 changed files with 93 additions and 16 deletions
|
@ -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')
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
:title="currentMedia.description"
|
||||
@touchstart.stop="mediaTouchStart"
|
||||
@touchmove.stop="mediaTouchMove"
|
||||
@touchend.stop="mediaTouchEnd"
|
||||
@click="hide"
|
||||
@load="onImageLoaded"
|
||||
>
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue