init
This commit is contained in:
359
src/components/GameSwiper.vue
Normal file
359
src/components/GameSwiper.vue
Normal file
@@ -0,0 +1,359 @@
|
||||
<template>
|
||||
<ModalTransition class="game-swiper" :show="show">
|
||||
<div class="scene-item item-1" @click="handleGoHome">
|
||||
<img src="../assets/images/new/go-home.png" alt="回到首页">
|
||||
</div>
|
||||
<div class="scene-item item-2">
|
||||
<img src="../assets/images/new/music.png" alt="音乐">
|
||||
</div>
|
||||
<div class="confirm-layout">
|
||||
<div class="scene-item item-3" @click="handleConfirmClick">
|
||||
<img src="../assets/images/new/confirm-btn.png" alt="确定">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="swiper-page">
|
||||
<div class="carousel-container">
|
||||
<div
|
||||
class="carousel-wrapper"
|
||||
@touchstart="handleTouchStart"
|
||||
@touchmove="handleTouchMove"
|
||||
@touchend="handleTouchEnd"
|
||||
>
|
||||
<div
|
||||
class="carousel-track"
|
||||
:style="{ transform: `translateX(calc(-${currentIndex * 100}% + ${currentIndex * 60}px))`, transition: isTransitioning ? 'transform 0.3s ease' : 'none' }"
|
||||
>
|
||||
<div
|
||||
v-for="(slide, index) in slides"
|
||||
:key="index"
|
||||
:class="['carousel-slide', { active: currentIndex === index }]"
|
||||
>
|
||||
<img :src="slide.image" :alt="slide.title" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 指示器 -->
|
||||
<div class="carousel-indicators">
|
||||
<span
|
||||
v-for="(slide, index) in slides"
|
||||
:key="index"
|
||||
:class="['indicator', { active: currentIndex === index }]"
|
||||
@click="goToSlide(index)"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalTransition>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import ModalTransition from "./ModalTransition.vue"
|
||||
import globalToastEvent, { ToastType } from '../globalToastEvent';
|
||||
|
||||
const props = defineProps({
|
||||
show: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:show'])
|
||||
const handleGoHome = () => {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
// 轮播图数据
|
||||
const slides = ref([
|
||||
{
|
||||
image: new URL('../assets/images/new/slider1.png', import.meta.url).href,
|
||||
title: '轮播图1',
|
||||
link: '/home' // 点击跳转的路由
|
||||
},
|
||||
{
|
||||
image: new URL('../assets/images/new/slider1.png', import.meta.url).href,
|
||||
title: '轮播图2',
|
||||
link: '/lottery'
|
||||
},
|
||||
{
|
||||
image: new URL('../assets/images/new/slider1.png', import.meta.url).href,
|
||||
title: '轮播图3',
|
||||
link: '/address'
|
||||
}
|
||||
])
|
||||
|
||||
const currentIndex = ref(0)
|
||||
const startX = ref(0)
|
||||
const moveX = ref(0)
|
||||
const isDragging = ref(false)
|
||||
const isTransitioning = ref(true)
|
||||
let autoPlayTimer = null
|
||||
|
||||
// 触摸开始
|
||||
const handleTouchStart = (e) => {
|
||||
startX.value = e.touches[0].clientX
|
||||
isDragging.value = true
|
||||
isTransitioning.value = false
|
||||
stopAutoPlay()
|
||||
}
|
||||
|
||||
// 触摸移动
|
||||
const handleTouchMove = (e) => {
|
||||
if (!isDragging.value) return
|
||||
moveX.value = e.touches[0].clientX - startX.value
|
||||
}
|
||||
|
||||
// 触摸结束
|
||||
const handleTouchEnd = () => {
|
||||
if (!isDragging.value) return
|
||||
|
||||
isTransitioning.value = true
|
||||
const threshold = 50 // 滑动阈值
|
||||
|
||||
if (moveX.value > threshold) {
|
||||
// 向右滑动 - 上一张
|
||||
prevSlide()
|
||||
} else if (moveX.value < -threshold) {
|
||||
// 向左滑动 - 下一张
|
||||
nextSlide()
|
||||
}
|
||||
|
||||
isDragging.value = false
|
||||
moveX.value = 0
|
||||
// startAutoPlay()
|
||||
}
|
||||
|
||||
// 上一张
|
||||
const prevSlide = () => {
|
||||
currentIndex.value = currentIndex.value > 0
|
||||
? currentIndex.value - 1
|
||||
: slides.value.length - 1
|
||||
}
|
||||
|
||||
// 下一张
|
||||
const nextSlide = () => {
|
||||
currentIndex.value = currentIndex.value < slides.value.length - 1
|
||||
? currentIndex.value + 1
|
||||
: 0
|
||||
}
|
||||
|
||||
// 跳转到指定幻灯片
|
||||
const goToSlide = (index) => {
|
||||
isTransitioning.value = true
|
||||
currentIndex.value = index
|
||||
stopAutoPlay()
|
||||
startAutoPlay()
|
||||
}
|
||||
|
||||
const hasVisitedBefore = localStorage.getItem('hasVisitedGameSwiper');
|
||||
|
||||
const handleConfirmClick = () => {
|
||||
const slide = slides.value?.[currentIndex.value]
|
||||
if (!slide) return
|
||||
handleSlideClick(slide)
|
||||
}
|
||||
|
||||
const handleSlideClick = (slide) => {
|
||||
if (!hasVisitedBefore) {
|
||||
localStorage.setItem('hasVisitedGameSwiper', 'true');
|
||||
globalToastEvent.emit(ToastType.SHOW_GAMEDEMO)
|
||||
globalToastEvent.emit(ToastType.SHOW_GAMEPAGE)
|
||||
} else {
|
||||
globalToastEvent.emit(ToastType.SHOW_GAMEPAGE)
|
||||
}
|
||||
}
|
||||
|
||||
// 自动播放
|
||||
const startAutoPlay = () => {
|
||||
autoPlayTimer = setInterval(() => {
|
||||
nextSlide()
|
||||
}, 3000) // 每3秒切换一次
|
||||
}
|
||||
|
||||
// 停止自动播放
|
||||
const stopAutoPlay = () => {
|
||||
if (autoPlayTimer) {
|
||||
clearInterval(autoPlayTimer)
|
||||
autoPlayTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
// startAutoPlay()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopAutoPlay()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.scene-item {
|
||||
position: fixed;
|
||||
cursor: pointer;
|
||||
transition: all 0.4s ease;
|
||||
overflow: hidden;
|
||||
animation: float 4s ease-in-out infinite;
|
||||
}
|
||||
.scene-item:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
.scene-item img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
.item-1 {
|
||||
width: 14vw;
|
||||
height: 14vw;
|
||||
top: 8vw;
|
||||
left: 2vw;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
.item-2 {
|
||||
width: 12vw;
|
||||
height: 12vw;
|
||||
top: 8vw;
|
||||
right: 2vw;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.item-3 {
|
||||
width: 46vw;
|
||||
bottom: 14vw;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.confirm-layout {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.swiper-page {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
background-image: url('../assets/images/new/swiper-page-bg.png');
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.carousel-container {
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
position: relative;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.carousel-wrapper {
|
||||
width: 80vw;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
touch-action: pan-y;
|
||||
}
|
||||
|
||||
.carousel-track {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.carousel-slide {
|
||||
min-width: calc(100% - 160px);
|
||||
width: calc(100% - 160px);
|
||||
margin: 0 20px;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
transform: scale(0.85);
|
||||
opacity: 0.5;
|
||||
filter: grayscale(60%) brightness(0.7);
|
||||
}
|
||||
|
||||
.carousel-slide.active {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
filter: grayscale(0%) brightness(1);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.carousel-slide img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 指示器 */
|
||||
.carousel-indicators {
|
||||
position: absolute;
|
||||
bottom: -8vw;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 2vw;
|
||||
padding: 2vw 4vw;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 4vw;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.indicator {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.indicator:hover {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.indicator.active {
|
||||
background: #fff;
|
||||
width: 28px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 移动端适配 */
|
||||
@media (max-width: 768px) {
|
||||
.game-page {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.carousel-container {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.carousel-slide {
|
||||
min-width: calc(100% - 12vw);
|
||||
width: calc(100% - 12vw);
|
||||
margin: 0 10px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.carousel-track {
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user