710
This commit is contained in:
+1104
File diff suppressed because it is too large
Load Diff
+94
-150
@@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
import { onMounted, onUnmounted, ref, useTemplateRef } from 'vue';
|
||||
import assets from '../assets';
|
||||
|
||||
import { regions, type RegionData, type Store } from '../data';
|
||||
import AniEle from '../components/AniEle.vue';
|
||||
|
||||
const regionsPos = [
|
||||
{ w: 68.8, h: 68.8, l: -1, t: -4, src: assets.p1.大北区, tt: '大北区', trt: -13, ad: 6, key: '大北区' },
|
||||
@@ -22,7 +23,7 @@ const selectedStoreIndex = ref(0);
|
||||
// 区域映射
|
||||
const regionKeyMap: { [key: string]: keyof RegionData | 'combined' } = {
|
||||
'大北区': '大北区',
|
||||
'大东区': '大东区',
|
||||
'大东区': '大东区',
|
||||
'大南区&免税': 'combined', // 需要合并大南区和免税
|
||||
'大西区': '大西区',
|
||||
'奥莱': 'combined', // 需要合并奥莱北区和奥莱东区
|
||||
@@ -51,9 +52,9 @@ function initializeStoreSelector() {
|
||||
// 等待DOM渲染完成
|
||||
setTimeout(() => {
|
||||
const storeItems = document.querySelectorAll('.store-item');
|
||||
|
||||
|
||||
if (storeItems.length === 0) return;
|
||||
|
||||
|
||||
// 创建 Intersection Observer
|
||||
observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
@@ -97,10 +98,25 @@ function cleanupObserver() {
|
||||
observer = null;
|
||||
}
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'startExploration', payload: { region: string; store: string, username: string }): void;
|
||||
}>();
|
||||
function startExploration() {
|
||||
// 这里可以添加开始探索的逻辑
|
||||
console.log('开始探索:', selectedStores.value[selectedStoreIndex.value]);
|
||||
if (!selectedRegion.value || selectedStores.value.length === 0) {
|
||||
alert('请选择一个区域和店铺!');
|
||||
return;
|
||||
}
|
||||
if ((document.querySelector('.name-input') as HTMLInputElement).value.trim() === '') {
|
||||
alert('请输入你的名字!');
|
||||
return;
|
||||
}
|
||||
emit('startExploration', {
|
||||
region: selectedRegion.value,
|
||||
store: selectedStores.value[selectedStoreIndex.value].店铺,
|
||||
username: (document.querySelector('.name-input') as HTMLInputElement).value
|
||||
});
|
||||
}
|
||||
|
||||
// 动画管理
|
||||
@@ -179,12 +195,12 @@ function createScaleInTextAnimation(element: Element, delay: number = 0) {
|
||||
// 气泡下降动画
|
||||
function createBubbleDropAnimation(element: Element, startTop: string, startLeft: string, endTop: string, endLeft: string, delay: number = 0, duration: number = 600) {
|
||||
const animation = element.animate([
|
||||
{
|
||||
{
|
||||
top: startTop,
|
||||
left: startLeft,
|
||||
transform: 'translate3d(-50%, -50%, 0) scale(0.1)'
|
||||
},
|
||||
{
|
||||
{
|
||||
top: endTop,
|
||||
left: endLeft,
|
||||
transform: 'translate3d(-50%, -50%, 0) scale(1)'
|
||||
@@ -200,8 +216,15 @@ function createBubbleDropAnimation(element: Element, startTop: string, startLeft
|
||||
return animation;
|
||||
}
|
||||
|
||||
const mainLogo = useTemplateRef('main-logo');
|
||||
|
||||
const mainLogoRules = ref([
|
||||
{ name: 'main', frame: 41, loop: 1, pauseAfter: true, duration: 33, reverse: false }
|
||||
]);
|
||||
// 反向播放所有动画来淡出
|
||||
function reverseAllAnimations() {
|
||||
mainLogoRules.value[0].reverse = true;
|
||||
|
||||
allAnimations.forEach(animation => {
|
||||
// 确保动画已经完成
|
||||
if (animation.playState === 'finished') {
|
||||
@@ -269,11 +292,11 @@ onMounted(() => {
|
||||
const startLeft = `${region.l / 2 + 50}%`;
|
||||
const endTop = `${region.t + 50}%`;
|
||||
const endLeft = `${region.l + 50}%`;
|
||||
|
||||
|
||||
// 设置初始位置
|
||||
(bubble as HTMLElement).style.top = startTop;
|
||||
(bubble as HTMLElement).style.left = startLeft;
|
||||
|
||||
|
||||
createBubbleDropAnimation(bubble, startTop, startLeft, endTop, endLeft, 0, 600 + region.ad * 100);
|
||||
|
||||
// 点击事件
|
||||
@@ -287,13 +310,13 @@ onMounted(() => {
|
||||
fill: 'forwards'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const pUp = () => {
|
||||
// 设置选中的区域和店铺数据
|
||||
selectedRegion.value = region.tt;
|
||||
selectedStores.value = getRegionStores(region.key);
|
||||
selectedStoreIndex.value = 0;
|
||||
|
||||
|
||||
bubble.querySelector('span')?.animate([
|
||||
{ opacity: 1 },
|
||||
{ opacity: 0 },
|
||||
@@ -302,7 +325,7 @@ onMounted(() => {
|
||||
easing: 'ease-in-out',
|
||||
fill: 'forwards'
|
||||
});
|
||||
|
||||
|
||||
const scaleAnimation = bubble.animate([
|
||||
{ transform: 'translate3d(-50%, -50%, 0) scale(0.92)', filter: 'brightness(0.9)' },
|
||||
{ transform: 'translate3d(-50%, -50%, 0) scale(4)', filter: 'brightness(1)', top: '53%', left: '50%' },
|
||||
@@ -331,14 +354,14 @@ onMounted(() => {
|
||||
// 动画完成后显示选择界面
|
||||
scaleAnimation.addEventListener('finish', () => {
|
||||
showStoreSelector.value = true;
|
||||
|
||||
|
||||
// 初始化 Intersection Observer
|
||||
initializeStoreSelector();
|
||||
});
|
||||
|
||||
bubble.removeEventListener('pointerdown', pDown);
|
||||
}
|
||||
|
||||
|
||||
bubble.addEventListener('pointerdown', pDown);
|
||||
bubble.addEventListener('pointerup', pUp, { once: true });
|
||||
});
|
||||
@@ -356,65 +379,14 @@ onUnmounted(() => {
|
||||
<section class="p1">
|
||||
<img :src="assets.标准logo" alt="" class="logo" style="top: 6%;left: 8%;width: 12%;opacity: 0;">
|
||||
<img :src="assets.p1.蓝天" alt="" class="bg">
|
||||
<div style="height: 12vw;top: 11.4%;left: 6%">
|
||||
<span class="green scale-in-ctn" style="border-radius: 6vw;font-size: 9vw; padding: 0 3vw;height: 100%;transform: scale(0);--base-delay: 0s;">
|
||||
<span class="scale-in-txt" style="transform: scale(0);">太阳脸</span>
|
||||
</span>
|
||||
<span class="green fake scale-in-txt" style="border-radius: 6vw;font-size: 9vw; padding: 0 3vw;height: 100%;transform: scale(0);--base-delay: 0s;">
|
||||
<span class="scale-in-txt" style="transform: scale(0);">太阳脸</span>
|
||||
</span>
|
||||
|
||||
<span class="green scale-in-ctn"
|
||||
style="width: 12vw;border-radius: 5vw; padding: 2vw 2vw 2vw 3vw; left: 34vw;height: 100%;transform: scale(0);--base-delay: 0s;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 91.18 90.18" preserveAspectRatio="xMidYMid meet">
|
||||
<path fill-rule="evenodd" stroke="rgb(255, 255, 255)" stroke-width="12.12px" stroke-linecap="round"
|
||||
stroke-linejoin="round" fill="none" d="M13.003,13.014 L13.003,71.540 L72.429,71.540 " />
|
||||
<path fill-rule="evenodd" stroke="rgb(255, 255, 255)" stroke-width="12.12px" stroke-linecap="round"
|
||||
stroke-linejoin="round" fill="none" d="M60.428,21.892 L14.854,70.801 " />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="green fake scale-in-txt"
|
||||
style="width: 12vw;border-radius: 5vw; padding: 2vw 2vw 2vw 3vw; left: 34vw;height: 100%;transform: scale(0);--base-delay: 0s;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 91.18 90.18" preserveAspectRatio="xMidYMid meet">
|
||||
<path fill-rule="evenodd" stroke="rgb(255, 255, 255)" stroke-width="12.12px" stroke-linecap="round"
|
||||
stroke-linejoin="round" fill="none" d="M13.003,13.014 L13.003,71.540 L72.429,71.540 " />
|
||||
<path fill-rule="evenodd" stroke="rgb(255, 255, 255)" stroke-width="12.12px" stroke-linecap="round"
|
||||
stroke-linejoin="round" fill="none" d="M60.428,21.892 L14.854,70.801 " />
|
||||
</svg>
|
||||
</span>
|
||||
<AniEle :url="assets.ani.主标出现" :width="1015" :height="336" :rules="mainLogoRules"
|
||||
style="top: 10%;width: 84%;left: 8%;" ref="main-logo" />
|
||||
|
||||
</div>
|
||||
<div style="left: 12%;top: 17.2%;width: 73vw;height: 7%;">
|
||||
<span class="black scale-in-ctn" style="border-radius: 7vw;height: 100%;width: 100%;transform: scale(0);--base-delay:0.1s">
|
||||
<span>弹性福利之旅</span>
|
||||
</span>
|
||||
<span class="black fake scale-in-txt" style="border-radius: 7vw;height: 100%;width: 100%;transform: scale(0);--base-delay:0.1s">
|
||||
<span>弹性福利之旅</span>
|
||||
</span>
|
||||
<span class="green scale-in-ctn"
|
||||
style="padding: 0 1.5vw;top: -17%;font-size: 3.5vw;right: -6%;height: 30%;border-radius: 3vw;transform: scale(0);--base-delay:0.2s">
|
||||
<span class="elan block relative">TOUR</span>
|
||||
</span>
|
||||
<span class="green fake scale-in-txt"
|
||||
style="padding: 0 1.5vw;top: -17%;font-size: 3.5vw;right: -6%;height: 30%;border-radius: 3vw;transform: scale(0);--base-delay:0.2s">
|
||||
<span class="elan block relative">TOUR</span>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<div style="top: 11.7%;left: 53%; display: flex; flex-direction: column;">
|
||||
<span class="white relative swap-in" style="left: 30%;height: 5.5vw;clip-path: inset(0 100% 0 0);--base-delay:0.6s">
|
||||
<span class="elan">FLEXIBEL</span>
|
||||
</span>
|
||||
<span class="white relative swap-in" style="height: 5.5vw;clip-path: inset(0 100% 0 0);--base-delay:0.9s">
|
||||
<span class="elan">BENEFITS</span>
|
||||
</span>
|
||||
</div>
|
||||
<!-- <img :src="assets.p1.太阳" alt="" style="top: 30%;left: 5%;width: 26%;"> -->
|
||||
<div style="width: 86vw;height: 86vw;top: 47%; left: 50%;transform: translate3d(-50%, -50%, 0); z-index: -1;">
|
||||
<div v-for="i in regionsPos" :style="{
|
||||
width: i.w + '%',
|
||||
width: i.w + '%',
|
||||
height: i.h + '%',
|
||||
transform: 'translate3d(-50%, -50%, 0) scale(0)',
|
||||
}" class="region-bubble">
|
||||
@@ -429,64 +401,62 @@ onUnmounted(() => {
|
||||
</div>
|
||||
|
||||
<div style="color: white; bottom: 16%;left: 50%;transform: translateX(-50%); font-size: 4.5vw;">
|
||||
<span class="block tag scale-in-ctn" style="background-color: #000;top: -1vw;left: -12vw;color: transparent;transform: scale(0);--base-delay:1s;">
|
||||
<span class="block tag scale-in-ctn"
|
||||
style="background-color: #000;top: -1vw;left: -12vw;color: transparent;transform: scale(0);--base-delay:1s;">
|
||||
<span>点击</span>
|
||||
</span>
|
||||
<span class="block tag scale-in-ctn" style="background-color: #304A7B;top: 4vw;left: -6vw;color: transparent;transform: scale(0);--base-delay:1.1s;">
|
||||
<span class="block tag scale-in-ctn"
|
||||
style="background-color: #304A7B;top: 4vw;left: -6vw;color: transparent;transform: scale(0);--base-delay:1.1s;">
|
||||
<span>选择</span>
|
||||
</span>
|
||||
<span class="block tag scale-in-ctn" style="background-color: #8FBE56;top: .8vw;left: 4.5vw;color: transparent;transform: scale(0);--base-delay:1.1s;">
|
||||
<span class="block tag scale-in-ctn"
|
||||
style="background-color: #8FBE56;top: .8vw;left: 4.5vw;color: transparent;transform: scale(0);--base-delay:1.1s;">
|
||||
<span>阵营</span>
|
||||
</span>
|
||||
<span class="block tag scale-in-txt" style="background-color: transparent;top: -1vw;left: -12vw;transform: scale(0);--base-delay:1.2s;">
|
||||
<span class="block tag scale-in-txt"
|
||||
style="background-color: transparent;top: -1vw;left: -12vw;transform: scale(0);--base-delay:1.2s;">
|
||||
<span>点击</span>
|
||||
</span>
|
||||
<span class="block tag scale-in-txt" style="background-color: transparent;top: 4vw;left: -6vw;transform: scale(0);--base-delay:1.2s;">
|
||||
<span class="block tag scale-in-txt"
|
||||
style="background-color: transparent;top: 4vw;left: -6vw;transform: scale(0);--base-delay:1.2s;">
|
||||
<span>选择</span>
|
||||
</span>
|
||||
<span class="block tag scale-in-txt" style="background-color: transparent;top: .8vw;left: 4.5vw;transform: scale(0);--base-delay:1.3s;">
|
||||
<span class="block tag scale-in-txt"
|
||||
style="background-color: transparent;top: .8vw;left: 4.5vw;transform: scale(0);--base-delay:1.3s;">
|
||||
<span>阵营</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 店铺选择界面 -->
|
||||
<div v-if="showStoreSelector" class="store-selector">
|
||||
<div class="store-selector-content">
|
||||
<h2 class="region-title">你来自{{ selectedRegion }}的</h2>
|
||||
|
||||
|
||||
<div class="store-list-container">
|
||||
<div class="store-list relative">
|
||||
<!-- 顶部填充,确保第一个元素能滚动到中间 -->
|
||||
<div class="scroll-padding relative"></div>
|
||||
|
||||
<div
|
||||
v-for="(store, index) in selectedStores"
|
||||
:key="store.店铺号"
|
||||
class="store-item relative"
|
||||
:class="{ active: index === selectedStoreIndex }"
|
||||
@click="selectStore(index)"
|
||||
>
|
||||
|
||||
<div v-for="(store, index) in selectedStores" :key="store.店铺号" class="store-item relative"
|
||||
:class="{ active: index === selectedStoreIndex }" @click="selectStore(index)">
|
||||
<div class="store-name relative">{{ store.店铺 }}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 底部填充,确保最后一个元素能滚动到中间 -->
|
||||
<div class="scroll-padding relative"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="name-input-section">
|
||||
<h3>请输入你的名字</h3>
|
||||
<div class="input-container">
|
||||
|
||||
<div class="name-input-section relative">
|
||||
<div class="title relative">请输入你的名字</div>
|
||||
<div class="input-container relative">
|
||||
<input type="text" class="name-input" placeholder="">
|
||||
<div class="input-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="start-button" @click="startExploration">
|
||||
<span class="arrow">→</span>
|
||||
<span>开始探索</span>
|
||||
</button>
|
||||
|
||||
<div class="action-buttons relative">
|
||||
<div class="start-button relative" @click="startExploration">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -579,22 +549,20 @@ img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.store-selector-content {
|
||||
top: 15%;
|
||||
left: 5%;
|
||||
width: 90%;
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.region-title {
|
||||
font-size: 6vw;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8vw;
|
||||
font-weight: 400;
|
||||
margin-bottom: 6vw;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@@ -603,20 +571,16 @@ img {
|
||||
height: 40vh;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
mask-image: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
black 15%,
|
||||
black 85%,
|
||||
transparent 100%
|
||||
);
|
||||
-webkit-mask-image: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
black 15%,
|
||||
black 85%,
|
||||
transparent 100%
|
||||
);
|
||||
mask-image: linear-gradient(to bottom,
|
||||
transparent 0%,
|
||||
black 15%,
|
||||
black 85%,
|
||||
transparent 100%);
|
||||
-webkit-mask-image: linear-gradient(to bottom,
|
||||
transparent 0%,
|
||||
black 15%,
|
||||
black 85%,
|
||||
transparent 100%);
|
||||
}
|
||||
|
||||
.store-list {
|
||||
@@ -628,7 +592,8 @@ img {
|
||||
}
|
||||
|
||||
.scroll-padding {
|
||||
height: calc(20vh - 4vw); /* 容器高度的一半减去项目高度的一半 */
|
||||
height: calc(20vh - 4vw);
|
||||
/* 容器高度的一半减去项目高度的一半 */
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -662,8 +627,6 @@ img {
|
||||
transform: translate(-50%, -50%);
|
||||
width: 90%;
|
||||
height: 10vw;
|
||||
border: 2px solid rgba(51, 51, 51, 0.2);
|
||||
border-radius: 10vw;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
@@ -674,8 +637,9 @@ img {
|
||||
margin-bottom: 8vw;
|
||||
}
|
||||
|
||||
.name-input-section h3 {
|
||||
.name-input-section .title {
|
||||
font-size: 5vw;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 4vw;
|
||||
font-weight: normal;
|
||||
@@ -696,26 +660,10 @@ img {
|
||||
outline: none;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.input-line {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: #333;
|
||||
margin-top: 1vw;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input-line::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -8px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 12px solid transparent;
|
||||
border-right: 12px solid transparent;
|
||||
border-bottom: 16px solid #333;
|
||||
background: url('../assets/p1/输入框.png');
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
@@ -726,17 +674,13 @@ img {
|
||||
}
|
||||
|
||||
.start-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3vw;
|
||||
padding: 4vw 8vw;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
font-size: 4.5vw;
|
||||
font-weight: bold;
|
||||
width: 40vw;
|
||||
height: 15vw;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
cursor: pointer;
|
||||
background-image: url('../assets/p1/开始探索.png');
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user