elk/components/modal/ModalMediaPreviewCarousel.vue
2023-01-06 23:46:36 +08:00

71 lines
2.4 KiB
Vue

<script setup lang="ts">
import { SwipeDirection } from '@vueuse/core'
import { useReducedMotion } from '@vueuse/motion'
import type { Attachment } from 'masto'
const { media = [], threshold = 20 } = defineProps<{
media?: Attachment[]
threshold?: number
}>()
const emit = defineEmits<{
(event: 'close'): void
}>()
const { modelValue } = defineModel<{
modelValue: number
}>()
const target = ref()
const animateTimeout = useTimeout(10)
const reduceMotion = useReducedMotion()
const canAnimate = computed(() => !reduceMotion.value && animateTimeout.value)
const { width, height } = useElementSize(target)
const { isSwiping, lengthX, lengthY, direction } = useSwipe(target, {
threshold: 5,
passive: false,
onSwipeEnd(e, direction) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
if (direction === SwipeDirection.RIGHT && Math.abs(distanceX.value) > threshold)
modelValue.value = Math.max(0, modelValue.value - 1)
// eslint-disable-next-line @typescript-eslint/no-use-before-define
if (direction === SwipeDirection.LEFT && Math.abs(distanceX.value) > threshold)
modelValue.value = Math.min(media.length - 1, modelValue.value + 1)
// eslint-disable-next-line @typescript-eslint/no-use-before-define
if (direction === SwipeDirection.UP && Math.abs(distanceY.value) > threshold)
emit('close')
},
})
const distanceX = computed(() => {
if (width.value === 0)
return 0
if (!isSwiping.value || (direction.value !== SwipeDirection.LEFT && direction.value !== SwipeDirection.RIGHT))
return modelValue.value * 100 * -1
return (lengthX.value / width.value) * 100 * -1 + (modelValue.value * 100) * -1
})
const distanceY = computed(() => {
if (height.value === 0 || !isSwiping.value || direction.value !== SwipeDirection.UP)
return 0
return (lengthY.value / height.value) * 100 * -1
})
</script>
<template>
<div ref="target" flex flex-row max-h-full max-w-full overflow-hidden>
<div flex :style="{ transform: `translateX(${distanceX}%) translateY(${distanceY}%)`, transition: isSwiping ? 'none' : canAnimate ? 'all 0.5s ease' : 'none' }">
<div v-for="item in media" :key="item.id" p4 select-none w-full flex-shrink-0 flex flex-col place-items-center>
<img max-h-full max-w-full :draggable="false" select-none :src="item.url || item.previewUrl" :alt="item.description || ''">
</div>
</div>
</div>
</template>