This commit is contained in:
yixu
2025-12-22 08:59:42 +08:00
parent ce179df652
commit 5731daf84a
18 changed files with 187 additions and 79 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 716 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

@@ -3,9 +3,6 @@
<div class="scene-item item-1" @click="handleGoBack">
<img src="../assets/images/new/back.png" alt="后退">
</div>
<div class="scene-item item-2">
<img src="../assets/images/new/music.png" alt="音乐">
</div>
<!-- 隐藏的圆形点击区域 -->
<div
@@ -18,35 +15,75 @@
height: circularArea.size
}"
></div>
<div class="game-page">
</div>
<div class="game-page" :style="gameBackgroundImg"></div>
</ModalTransition>
<Lottery :show="lotteryShow" @close="lotteryShow = false" :type="lotteryType" :data="lotteryNoticeData" />
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { ref, onMounted, onUnmounted, computed } from 'vue'
import ModalTransition from "./ModalTransition.vue"
import Lottery from './Lottery.vue'
import globalToastEvent, { ToastType } from '../globalToastEvent';
import game1 from '../assets/images/new/game1.png';
import game2 from '../assets/images/new/game2.png';
import game3 from '../assets/images/new/game3.png';
import game4 from '../assets/images/new/game4.png';
import game5 from '../assets/images/new/game5.png';
import { globalStore } from "../globalstore.js";
import { Request } from "../libs/utils"
const props = defineProps({
show: false,
gameSlideId: 1
})
const emit = defineEmits(['close'])
// Lottery 相关状态
const lotteryShow = ref(false)
const lotteryType = ref('draw')
const lotteryNoticeData = ref(null)
const gameImgMap = {
1: game1,
2: game2,
3: game3,
4: game4,
5: game5
}
const gameBackgroundImg = computed(() => {
const id = Number(props.gameSlideId) || 1;
const img = gameImgMap[id] || game1;
return { backgroundImage: `url(${img})` };
})
const circularAreaList = {
1: {
top: '62vw',
left: '55vw',
size: '35vw'
},
2: {
top: '62vw',
left: '80vw',
size: '35vw'
},
3: {
top: '88vw',
left: '48vw',
size: '35vw'
},
4: {
top: '96vw',
left: '36vw',
size: '35vw'
},
5: {
top: '96vw',
left: '40vw',
size: '35vw'
}
}
// 隐藏圆形点击区域的配置(可调整坐标和大小)
const circularArea = ref({
top: '78vw', // 可配置:距离顶部的位置
left: '50vw', // 可配置:距离左边的位置
size: '25vw' // 可配置:圆形区域的大小
const circularArea = computed(() => {
const id = Number(props.gameSlideId) || 1;
return circularAreaList[id] || circularAreaList[1];
})
const handleGoBack = () => {
@@ -54,8 +91,20 @@ const handleGoBack = () => {
}
// 点击隐藏区域显示抽奖弹窗
const handleShowLottery = () => {
globalToastEvent.emit(ToastType.SHOW_LOTTERY)
const handleShowLottery = async () => {
const loading = weui.loading()
globalToastEvent.emit(ToastType.SHOW_LOTTERY);
// 点击隐藏区域后游戏结束再次调用game log接口
const logId = globalStore.current_game_log_id;
let gameLogRes = await Request("game/log", { source: globalStore.game_id, log_id: logId }, "POST");
if (!gameLogRes || !gameLogRes.res || (gameLogRes.res.status !== 200 && gameLogRes.res.status !== 201)) {
emit('close')
loading.hide()
return
} else {
console.log(gameLogRes)
}
}
// 轮播图数据
@@ -236,7 +285,6 @@ onUnmounted(() => {
justify-content: center;
padding: 20px;
box-sizing: border-box;
background-image: url('../assets/images/new/game-1-bg.png');
background-size: cover;
background-repeat: no-repeat;
background-position: center;

View File

@@ -3,10 +3,8 @@
<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="confirm-layout" v-if="isConfirmBtnDisplay">
<div class="scene-item item-3" @click="handleConfirmClick">
<img src="../assets/images/new/confirm-btn.png" alt="确定">
</div>
@@ -22,13 +20,15 @@
>
<div
class="carousel-track"
:style="{ transform: `translateX(calc(-${currentIndex * 100}% + ${currentIndex * 60}px))`, transition: isTransitioning ? 'transform 0.3s ease' : 'none' }"
:style="{ transform: `translateX(calc(-${currentIndex * 104}% + ${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 v-show="slide.show" class="complete-icon" src="../assets/images/new/complete-icon.png" alt="">
<img :src="slide.image" :alt="slide.title" />
</div>
</div>
@@ -49,38 +49,91 @@
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { ref, onMounted, onUnmounted, watch, computed } from 'vue'
import ModalTransition from "./ModalTransition.vue"
import globalToastEvent, { ToastType } from '../globalToastEvent';
import { Request } from "../libs/utils"
import slider1 from '../assets/images/new/slider1.png';
import slider2 from '../assets/images/new/slider2.png';
import slider3 from '../assets/images/new/slider3.png';
import slider4 from '../assets/images/new/slider4.png';
import slider5 from '../assets/images/new/slider5.png';
import { globalStore } from "../globalstore.js";
const props = defineProps({
show: false,
})
const emit = defineEmits(['update:show'])
const handleGoHome = () => {
emit('close')
}
const emit = defineEmits(['close'])
// 轮播图数据
const slides = ref([
{
image: new URL('../assets/images/new/slider1.png', import.meta.url).href,
image: slider1,
title: '轮播图1',
link: '/home' // 点击跳转的路由
id: 1,
show: true,
},
{
image: new URL('../assets/images/new/slider1.png', import.meta.url).href,
image: slider2,
title: '轮播图2',
link: '/lottery'
id: 2,
show: true,
},
{
image: new URL('../assets/images/new/slider1.png', import.meta.url).href,
image: slider3,
title: '轮播图3',
link: '/address'
id: 3,
show: true,
},
{
image: slider4,
title: '轮播图4',
id: 4,
show: true,
},
{
image: slider5,
title: '轮播图5',
id: 5,
show: true,
}
])
const isConfirmBtnDisplay = computed(() => {
const slide = slides.value?.[currentIndex.value];
return !globalStore.completed_games_today.includes(`game${slide.id}`);
})
const getGameInfo = async () => {
const result = await Request('game/info', { refresh_official: true, refresh_cap_scan: true }, "GET")
if (result?.res?.status === 200) {
console.log(result)
// globalStore.completed_games_today = result.json.completed_games_today || [];
globalStore.completed_games_today = ['game1', 'game3']; // 测试数据
slides.value = slides.value.map(slide => ({
...slide,
show: globalStore.completed_games_today.includes(`game${slide.id}`)
}));
} else {
weui.alert("获取游戏数据失败,请刷新页面重试");
emit('close');
return
}
}
watch(() => props.show, async (newVal) => {
if (!newVal) {
return
}
getGameInfo();
}, { immediate: true })
const handleGoHome = () => {
emit('close')
}
const currentIndex = ref(0)
const startX = ref(0)
const moveX = ref(0)
@@ -141,24 +194,37 @@ const goToSlide = (index) => {
isTransitioning.value = true
currentIndex.value = index
stopAutoPlay()
startAutoPlay()
// startAutoPlay()
}
const hasVisitedBefore = localStorage.getItem('hasVisitedGameSwiper');
const handleConfirmClick = () => {
const handleConfirmClick = async () => {
const loading = weui.loading()
const slide = slides.value?.[currentIndex.value]
if (!slide) return
handleSlideClick(slide)
// 点击确定按钮开始游戏调用一次game log接口
const gameSource = `game${slide.id}`;
globalStore.game_id = gameSource;
let gameLogRes = await Request("game/log", { source: globalStore.game_id }, "POST");
if (!gameLogRes || !gameLogRes.res || (gameLogRes.res.status !== 200 && gameLogRes.res.status !== 201)) {
emit('close')
loading.hide()
return
} else {
globalStore.current_game_log_id = gameLogRes.json.log_id;
}
}
const handleSlideClick = (slide) => {
if (!hasVisitedBefore) {
localStorage.setItem('hasVisitedGameSwiper', 'true');
globalToastEvent.emit(ToastType.SHOW_GAMEDEMO)
globalToastEvent.emit(ToastType.SHOW_GAMEPAGE)
globalToastEvent.emit(ToastType.SHOW_GAMEPAGE, slide)
} else {
globalToastEvent.emit(ToastType.SHOW_GAMEPAGE)
globalToastEvent.emit(ToastType.SHOW_GAMEPAGE, slide)
}
}
@@ -188,6 +254,12 @@ onUnmounted(() => {
</script>
<style scoped>
.complete-icon {
width: 14vw !important;
position: absolute;
left: 2vw;
top: -2vw;
}
.scene-item {
position: fixed;
cursor: pointer;
@@ -207,8 +279,8 @@ onUnmounted(() => {
.item-1 {
width: 14vw;
height: 14vw;
top: 8vw;
left: 2vw;
top: 6.8vw;
left: 3vw;
animation-delay: 0s;
}
.item-2 {

View File

@@ -301,7 +301,7 @@ const handleLoginSuccess = async () => {
console.log("已登录")
loginShow.value = false
await initUserGameInfos(true, true)
// await initUserGameInfos(true, true)
await getNotice()
}
@@ -397,7 +397,10 @@ globalToastEvent.on(ToastType.SHOW_POPUPMORE, () => {
})
const gamePageShow = ref(false)
globalToastEvent.on(ToastType.SHOW_GAMEPAGE, () => {
const gameSlideId = ref('')
globalToastEvent.on(ToastType.SHOW_GAMEPAGE, (slide) => {
console.log("slide in home page:", slide.id);
gameSlideId.value = slide.id;
gamePageShow.value = true
})
@@ -474,7 +477,7 @@ const handleAddress = (id) => {
<Login :show="loginShow" @login-success="handleLoginSuccess" />
<GameSwiper v-model:show="gameSwiperShow" @close="gameSwiperShow = false" />
<Todolist :show="todolistShow" @close="todolistShow = false"></Todolist>
<GamePage :show="gamePageShow" @close="gamePageShow = false" />
<GamePage :show="gamePageShow" @close="gamePageShow = false" :gameSlideId="gameSlideId" />
<GameDemo :show="gameDemoShow" @close="gameDemoShow = false" />
<Lottery :show="lotteryShow" @close="lotteryShow = false" :type="lotteryType" :data="lotteryNoticeData"></Lottery>
<SharePage :show="sharePageShow" @close="sharePageShow = false"></SharePage>
@@ -618,9 +621,10 @@ const handleAddress = (id) => {
.item-4 {
width: 11vw;
top: 1.5%;
right: 7%;
top: 8vw;
right: 4vw;
animation-delay: 0s;
z-index: 999;
}
.item-5 {

View File

@@ -7,29 +7,12 @@
<div class="lottery-card" ref="cardRef">
<div class="lottery-prize">
<div class="lottery-layer" ref="layerRef">
<!-- 底层中奖图片被遮罩层盖住主要用于语义与对齐 -->
<div class="lottery-image" :class="'USER_' + activePrizeData.prize_code" />
<!-- 中层遮罩层背景默认完全遮住中奖图 -->
<div class="lottery-mask-image" ref="maskBgRef" />
<!-- 顶层通过 mask-img.png 显示中奖图片的一小块并自动放大揭晓 -->
<div
class="lottery-reveal lottery-image"
:class="[
'USER_' + activePrizeData.prize_code,
revealDone ? 'reveal-done' : ''
]"
ref="revealRef"
v-show="activePrizeData.prize_code !== 'NO'"
/>
<!-- 可视化的 mask否则用户看不到 mask-img.png 本身 -->
<div
class="lottery-mask-visual"
:class="revealDone ? 'reveal-done' : ''"
v-show="activePrizeData.prize_code !== 'NO'"
/>
<div class="lottery-mask-image" ref="maskBgRef">
<div class="lottery-reveal lottery-image" v-show="activePrizeData.prize_code !== 'NO'">
<div class="lottery-image" :class="'USER_' + activePrizeData.prize_code"></div>
</div>
</div>
</div>
<!-- 标题保持原逻辑 -->
@@ -137,7 +120,7 @@ const initAnimateStyle = () => {
keyframes.push({
x: gsap.utils.random(-strength, strength),
y: gsap.utils.random(-strength, strength),
duration: 0.05 // 每帧的持续时间
duration: 0.01 // 每帧的持续时间
});
}
@@ -245,9 +228,8 @@ watch(() => props.show, async (newVal) => {
}
});
prizeshowTime.to([sceneRef.value], {
duration: 2.5,
duration: 1.5,
scale: 1,
opacity: 1,
ease: "power4.inOut"
@@ -269,7 +251,7 @@ watch(() => props.show, async (newVal) => {
onComplete: () => {
revealDone.value = true
// 动画结束后把遮罩背景淡出,让中奖图完全可见
maskBgRef.value && gsap.to(maskBgRef.value, { duration: 0.3, opacity: 0 })
maskBgRef.value && gsap.to(maskBgRef.value, { duration: 0.3 })
}
})
}
@@ -353,7 +335,6 @@ onUnmounted(() => {
.lottery-image {
height: 110vw;
width: 82vw;
margin-top: -30vw;
position: absolute;
inset: 0;
background-position: center;
@@ -368,7 +349,7 @@ onUnmounted(() => {
z-index: 3;
/* CSS MaskWebKit + 标准) */
/* -webkit-mask-image: url("../assets/images/new/mask-img.png");
-webkit-mask-image: url("../assets/images/new/mask-img.png");
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center;
-webkit-mask-size: var(--maskSize) var(--maskSize);
@@ -376,9 +357,9 @@ onUnmounted(() => {
mask-image: url("../assets/images/new/mask-img.png");
mask-repeat: no-repeat;
mask-position: center;
mask-size: var(--maskSize) var(--maskSize); */
-webkit-clip-path: circle(calc(var(--maskSize) * 0.5) at 50% 50%);
clip-path: circle(calc(var(--maskSize) * 0.5) at 50% 50%);
mask-size: var(--maskSize) var(--maskSize);
/* -webkit-clip-path: circle(calc(var(--maskSize) * 0.5) at 50% 50%);
clip-path: circle(calc(var(--maskSize) * 0.5) at 50% 50%); */
}
/* 可视化的 mask-img.png让用户能看到“遮罩在放大”的过程 */

View File

@@ -1,6 +1,9 @@
import { reactive } from "vue"
export const globalStore = reactive({
completed_games_today: [],
current_game_log_id: '',
game_id: '',
first_share_today:false,
globalAudio: null,
userMutedMusic: false, // 用户是否手动静音了音乐