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 () {
|
created () {
|
||||||
this.mediaSwipeGestureRight = GestureService.swipeGesture(
|
this.mediaGesture = new GestureService.SwipeAndScaleGesture({
|
||||||
GestureService.DIRECTION_RIGHT,
|
direction: GestureService.DIRECTION_LEFT,
|
||||||
this.goPrev,
|
callbackPositive: this.goNext,
|
||||||
50
|
callbackNegative: this.goPrev,
|
||||||
)
|
threshold: 50
|
||||||
this.mediaSwipeGestureLeft = GestureService.swipeGesture(
|
})
|
||||||
GestureService.DIRECTION_LEFT,
|
|
||||||
this.goNext,
|
|
||||||
50
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getType (media) {
|
getType (media) {
|
||||||
return fileTypeService.fileType(media.mimetype)
|
return fileTypeService.fileType(media.mimetype)
|
||||||
},
|
},
|
||||||
mediaTouchStart (e) {
|
mediaTouchStart (e) {
|
||||||
GestureService.beginSwipe(e, this.mediaSwipeGestureRight)
|
this.mediaGesture.start(e)
|
||||||
GestureService.beginSwipe(e, this.mediaSwipeGestureLeft)
|
|
||||||
},
|
},
|
||||||
mediaTouchMove (e) {
|
mediaTouchMove (e) {
|
||||||
GestureService.updateSwipe(e, this.mediaSwipeGestureRight)
|
this.mediaGesture.move(e)
|
||||||
GestureService.updateSwipe(e, this.mediaSwipeGestureLeft)
|
},
|
||||||
|
mediaTouchEnd (e) {
|
||||||
|
this.mediaGesture.end(e)
|
||||||
},
|
},
|
||||||
hide () {
|
hide () {
|
||||||
this.$store.dispatch('closeMediaViewer')
|
this.$store.dispatch('closeMediaViewer')
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
:title="currentMedia.description"
|
:title="currentMedia.description"
|
||||||
@touchstart.stop="mediaTouchStart"
|
@touchstart.stop="mediaTouchStart"
|
||||||
@touchmove.stop="mediaTouchMove"
|
@touchmove.stop="mediaTouchMove"
|
||||||
|
@touchend.stop="mediaTouchEnd"
|
||||||
@click="hide"
|
@click="hide"
|
||||||
@load="onImageLoaded"
|
@load="onImageLoaded"
|
||||||
>
|
>
|
||||||
|
|
|
@ -4,9 +4,17 @@ const DIRECTION_RIGHT = [1, 0]
|
||||||
const DIRECTION_UP = [0, -1]
|
const DIRECTION_UP = [0, -1]
|
||||||
const DIRECTION_DOWN = [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 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])
|
const vectorLength = v => Math.sqrt(v[0] * v[0] + v[1] * v[1])
|
||||||
|
|
||||||
|
@ -61,6 +69,76 @@ const updateSwipe = (event, gesture) => {
|
||||||
gesture._swiping = false
|
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 = {
|
const GestureService = {
|
||||||
DIRECTION_LEFT,
|
DIRECTION_LEFT,
|
||||||
DIRECTION_RIGHT,
|
DIRECTION_RIGHT,
|
||||||
|
@ -68,7 +146,8 @@ const GestureService = {
|
||||||
DIRECTION_DOWN,
|
DIRECTION_DOWN,
|
||||||
swipeGesture,
|
swipeGesture,
|
||||||
beginSwipe,
|
beginSwipe,
|
||||||
updateSwipe
|
updateSwipe,
|
||||||
|
SwipeAndScaleGesture
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GestureService
|
export default GestureService
|
||||||
|
|
Loading…
Reference in a new issue