feat: add fullscreen viewer

This commit is contained in:
2025-08-08 00:25:27 +03:00
parent d9fd26d354
commit 4ae8d59328
11 changed files with 271 additions and 147 deletions

View File

@@ -0,0 +1,67 @@
<template>
<div class="fixed z-200 top-0 inset-0 flex justify-center align-center flex-col h-full bg-black">
<Swiper
:zoom="true"
:navigation="true"
:pagination="{
type: 'fraction',
}"
:activeIndex="activeIndex"
:modules="[Zoom, Navigation, Pagination]"
class="mySwiper w-full h-full"
@slider-move="vibrate"
>
<SwiperSlide v-for="image in images">
<div class="swiper-zoom-container">
<img :src="image.largeURL" :alt="image.alt"/>
</div>
</SwiperSlide>
</Swiper>
<button
class="absolute z-50 text-white text-xl right-5 cursor-pointer"
style="top: calc(var(--tg-safe-area-inset-top, 5px) + var(--tg-content-safe-area-inset-top, 5px))"
@click="onClose"
>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="size-10">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"/>
</svg>
</button>
</div>
</template>
<script setup>
import {Navigation, Pagination, Zoom} from "swiper/modules";
import {Swiper, SwiperSlide} from "swiper/vue";
import {onMounted} from "vue";
const props = defineProps({
images: {
type: Array,
default: () => [],
},
activeIndex: {
type: Number,
default: 0,
},
});
const emits = defineEmits(['close']);
let canVibrate = true;
function vibrate() {
if (!canVibrate) return;
window.Telegram.WebApp.HapticFeedback.impactOccurred('soft');
canVibrate = false;
setTimeout(() => {
canVibrate = true;
}, 50);
}
function onClose() {
window.Telegram.WebApp.HapticFeedback.impactOccurred('medium');
emits('close');
}
</script>

View File

@@ -1,100 +1,49 @@
<template>
<div class="aspect-w-4 aspect-h-3">
<swiper
:lazy="true"
:pagination="pagination"
:navigation="true"
:modules="modules"
class="mySwiper w-full min-h-[200px]"
@touchMove="onTouchMove"
<swiper-container ref="swiperEl" pagination="true">
<swiper-slide
v-for="image in images"
lazy
>
<swiper-slide v-for="image in images">
<img
:src="image.url"
:alt="image.alt"
loading="lazy"
/>
<div
class="swiper-lazy-preloader swiper-lazy-preloader-white"
></div>
</swiper-slide>
</swiper>
</div>
<img
:src="image.url"
:alt="image.alt"
loading="lazy"
/>
</swiper-slide>
</swiper-container>
</template>
<script>
import {Swiper, SwiperSlide} from 'swiper/vue';
<script setup>
import {onMounted, ref} from "vue";
import 'swiper/css';
import 'swiper/css/pagination';
import {Pagination} from 'swiper/modules';
export default {
components: {
Swiper,
SwiperSlide,
const props = defineProps({
images: {
type: Array,
default: () => [],
},
});
props: {
images: {
type: Array,
default: () => [],
}
},
const swiperEl = ref(null);
setup() {
const throttle = (func, delay) => {
let lastCall = 0;
return (...args) => {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
func(...args);
}
};
};
let haptic = true;
const hapticTick = throttle(() => {
const haptic = window?.Telegram?.WebApp?.HapticFeedback;
if (haptic?.selectionChanged) {
haptic.selectionChanged();
} else if (haptic?.impactOccurred) {
haptic.impactOccurred('light');
}
}, 100);
onMounted(async () => {
swiperEl.value?.addEventListener('swiperactiveindexchange', (event) => {
window.Telegram.WebApp.HapticFeedback.selectionChanged();
});
const onTouchMove = () => {
hapticTick();
};
swiperEl.value?.addEventListener('swiperprogress', (num) => {
if (haptic === false) return;
window.Telegram.WebApp.HapticFeedback.impactOccurred('light');
haptic = false;
setTimeout(() => haptic = true, 50);
});
return {
pagination: {
clickable: true,
dynamicBullets: true,
},
modules: [Pagination],
onTouchMove,
};
},
};
swiperEl.value?.addEventListener('swiperreachbeginning', (event) => {
window.Telegram.WebApp.HapticFeedback.impactOccurred('medium');
});
swiperEl.value?.addEventListener('swiperreachend', (event) => {
window.Telegram.WebApp.HapticFeedback.impactOccurred('medium');
});
});
</script>
<style scoped>
.product-swiper {
width: 100%;
height: auto;
}
.swiper-slide {
text-align: center;
}
img {
width: 100%;
display: block;
object-fit: contain;
}
</style>