arcteryx-game/src/App.vue
2025-07-16 11:12:26 +08:00

219 lines
4.4 KiB
Vue

<script setup lang="ts">
import { ref, useTemplateRef, watch } from 'vue';
import Game from './pages/Game.vue';
import Page1 from './pages/Page1.vue';
import assets from './assets';
import Loader from './pages/Loader.vue';
const stage = ref(1);
const userData = ref({
region: '奥莱',
store: '北京斯普瑞斯',
username: '测试用户'
});
const gamePage = useTemplateRef('game-page');
function startExploration(payload: { region: string; store: string; username: string }) {
userData.value = payload;
stage.value = 1;
gamePage.value?.init();
}
function cloudUp() {
document.querySelectorAll('.cloud').forEach((ele) => {
ele.animate([
{ top: '300vw' },
], {
duration: 1000,
easing: 'ease-in',
fill: 'forwards',
});
});
}
function cloudDown() {
document.querySelectorAll('.cloud.all').forEach((ele) => {
ele.animate([
{ top: '-50vw' },
// @ts-ignore
{ top: ele.style.top },
], {
duration: 1000,
easing: 'ease-in',
fill: 'forwards',
});
});
}
let audio = new Audio(assets.bgm);
audio.loop = true;
audio.volume = 0.5;
const playingBgm = ref(false);
watch(playingBgm, (val) => {
if (val) {
audio.play();
} else {
audio.pause();
}
});
//std radio is 2462 / 1179
const scale = ref(1);
const onResize = () => {
let radio = window.innerHeight / window.innerWidth;
let stdRadio = 2462 / 1179;
if (radio < stdRadio * 0.88) {
scale.value = radio / stdRadio;
} else {
scale.value = 1;
}
};
onResize();
const isMobile = navigator.userAgent.match(/(iPhone|iPod|Android|ios|Mobile)/i);
if (!isMobile) window.addEventListener('resize', onResize);
const gameKey = ref(10)
</script>
<template>
<main :style="{ scale: scale }">
<div class="bg"> </div>
<img class="cloud cloud-top" src="./assets/game/云.png" v-for="c in [
{ w: 60, t: 10, l: 75, all: true },
{ w: 75, t: 55, l: -45 },
{ w: 40, t: 105, l: 35 },
]" :style="{ width: `${c.w}vw`, top: `${c.t}vw`, left: `${c.l}vw` }"
:class="{ all: c.all, left: c.l < 0, right: c.l > 0 }"></img>
<img class="cloud cloud-bottom" src="./assets/game/云.png" v-for="c in [
{ w: 75, t: 165, l: -35, all: true },
{ w: 100, t: 144, l: 60, all: true },
]" :style="{ width: `${c.w}vw`, top: `${c.t}vw`, left: `${c.l}vw` }"
:class="{ all: c.all, left: c.l < 0, right: c.l > 0 }"></img>
<Loader class="page" v-if="stage == -1" @loaded="stage = 0"
style="z-index: 100;" />
<Transition name="fade" mode="out-in">
<Page1 class="page page-1" v-if="stage == 0" style="z-index: 99;"
@start-exploration="startExploration" />
</Transition>
<Game class="page" :userdata="userData" :key="gameKey"
:style="{ opacity: stage === 1 ? 1 : 0 }" style="z-index: 98;"
@cloud-down="cloudDown" @cloud-up="cloudUp" ref="game-page" @restart="gameKey++" />
<div class="overlay page">
<div class="bgm"
style="position: absolute; top: 16vw;right: 8vw;width: 8vw; cursor: pointer;pointer-events: all;">
<img src="./assets/开启音乐.png" @click="playingBgm = false" alt=""
v-if="playingBgm">
<img src="./assets/关掉音乐.png" @click="playingBgm = true" alt="" v-else>
</div>
</div>
</main>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
<style scoped>
.overlay {
pointer-events: none;
}
main {
height: 100vh;
height: 100dvh;
width: 100vw;
overflow: visible;
position: relative;
}
.bgm {
img {
width: 100%;
}
}
@keyframes left-slide-in {
from {
transform: translateX(-300%);
}
to {
transform: translateX(0);
}
}
@keyframes right-slide-in {
from {
transform: translateX(300%);
}
to {
transform: translateX(0);
}
}
.cloud {
position: absolute;
&.left {
animation: left-slide-in 0.8s forwards;
}
&.right {
animation: right-slide-in 0.8s forwards;
}
}
.bg {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
height: calc(2462 / 1179 * 100vw);
width: 100%;
z-index: -1;
background-image: url('./assets/game/蓝天.png');
background-size: cover;
background-position: center;
}
.page {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 100%;
height: calc(2462 / 1179 * 100vw);
}
.page-1 {
transition: 0.7s all;
}
</style>