add audio effects

This commit is contained in:
feie9456 2025-07-25 16:04:33 +08:00
parent 9c722afe9d
commit 32c01bf868
11 changed files with 218 additions and 76 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -0,0 +1,19 @@
import { EasyAudio } from "./utils";
import from "./按钮音效.mp3"
import from "./弹簧蓄力结束弹射有效.mp3"
import from "./弹簧蓄力下拉音效.mp3"
import from "./风声.mp3"
import from "./首页标题、尾标出现音效.mp3"
import from "./最终结算音效.mp3"
const ea = new EasyAudio([
{name: "按钮音效", audioUrl: 按钮音效, volume: 0.5},
{name: "弹簧蓄力结束弹射", audioUrl: 弹簧蓄力结束弹射, volume: 0.5},
{name: "弹簧蓄力下拉音效", audioUrl: 弹簧蓄力下拉音效, volume: 0.5},
{name: "风声", audioUrl: 风声, volume: 0.5},
{name: "标题出现", audioUrl: 标题出现, volume: 0.5},
{name: "结算", audioUrl: 结算, volume: 0.5},
]);
export default ea;

114
src/assets/sounds/utils.ts Normal file
View File

@ -0,0 +1,114 @@
export class EasyAudio {
private static audioCtx: AudioContext | null = null;
private buffers: Map<string, AudioBuffer> = new Map();
private sources: Map<string, AudioBufferSourceNode> = new Map();
private audioOptions: Map<
string,
{ audioUrl: string; loop?: boolean; volume?: number }
> = new Map();
private get audioCtx(): AudioContext {
if (!EasyAudio.audioCtx) {
EasyAudio.audioCtx = new AudioContext();
}
return EasyAudio.audioCtx;
}
constructor(
audios?:
| { name: string; audioUrl: string | URL; loop?: boolean; volume?: number }
| { name: string; audioUrl: string | URL; loop?: boolean; volume?: number }[]
) {
if (audios) {
this.add(audios);
}
}
private async createAudioBuffer(audioUrl: string): Promise<AudioBuffer> {
const response = await fetch(audioUrl);
const arrayBuffer = await response.arrayBuffer();
return await this.audioCtx.decodeAudioData(arrayBuffer);
}
private async loadAudio(name: string) {
const options = this.audioOptions.get(name);
if (!options) {
throw new Error(`音频 ${name} 未找到`);
}
const buffer = await this.createAudioBuffer(options.audioUrl);
this.buffers.set(name, buffer);
}
async load(): Promise<void> {
const promises = Array.from(this.audioOptions.keys()).map(name =>
this.loadAudio(name)
);
await Promise.all(promises);
}
add(
audios:
| { name: string; audioUrl: string | URL; loop?: boolean; volume?: number }
| { name: string; audioUrl: string | URL; loop?: boolean; volume?: number }[]
): void {
if (Array.isArray(audios)) {
audios.forEach(audio => {
this.audioOptions.set(audio.name, {
audioUrl: typeof (audio.audioUrl) === "string" ? audio.audioUrl : audio.audioUrl.href,
loop: audio.loop,
volume: audio.volume,
});
});
} else {
this.audioOptions.set(audios.name, {
audioUrl: typeof (audios.audioUrl) === "string" ? audios.audioUrl : audios.audioUrl.href,
loop: audios.loop,
volume: audios.volume,
});
}
}
async play(name: string) {
if (!this.buffers.has(name)) {
await this.loadAudio(name);
}
const buffer = this.buffers.get(name);
const options = this.audioOptions.get(name);
if (!buffer || !options) {
throw new Error(`音频 ${name} 未找到`);
}
const source = this.audioCtx.createBufferSource();
source.buffer = buffer;
source.loop = options.loop || false;
let gainNode: GainNode | null = null;
if (options.volume !== undefined) {
gainNode = this.audioCtx.createGain();
gainNode.gain.value = options.volume;
source.connect(gainNode);
gainNode.connect(this.audioCtx.destination);
} else {
source.connect(this.audioCtx.destination);
}
source.start();
this.sources.set(name, source);
console.log(`音频 ${name} 播放`);
}
stop(name: string) {
const source = this.sources.get(name);
if (source) {
source.stop();
this.sources.delete(name);
}
}
stopAll() {
this.sources.forEach(source => source.stop());
this.sources.clear();
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -11,6 +11,7 @@ defineExpose({
} }
}) })
import AudioEffects from '../assets/sounds'
const props = defineProps<{ const props = defineProps<{
userdata: { userdata: {
@ -32,6 +33,7 @@ const hasP2AnimationPlayed = ref(false);
async function shoot() { async function shoot() {
//sunEle.value?.jumpTo('') //sunEle.value?.jumpTo('')
AudioEffects.play("弹簧蓄力下拉音效")
sunEle.value?.jumpTo('蓄力飞') sunEle.value?.jumpTo('蓄力飞')
await document.querySelector('.sun-ani-wrapper')?.animate([ await document.querySelector('.sun-ani-wrapper')?.animate([
@ -54,6 +56,7 @@ async function shoot() {
document.querySelector('.arrow')?.remove(); document.querySelector('.arrow')?.remove();
}); });
emit('cloudUp'); emit('cloudUp');
AudioEffects.play("弹簧蓄力结束弹射")
document.querySelector('.bed')?.animate([ document.querySelector('.bed')?.animate([
{ transform: 'translateY(0)' }, { transform: 'translateY(0)' },
{ transform: 'translateY(170%)' }, { transform: 'translateY(170%)' },
@ -300,6 +303,7 @@ function gToggle(pos: 'left' | 'center' | 'right') {
if (windEl) { if (windEl) {
windEl.style.rotate = `${windAni[0]}deg`; windEl.style.rotate = `${windAni[0]}deg`;
} }
AudioEffects.play("风声")
windEl?.animate([ windEl?.animate([
{ left: windAni[1][0] + 'vw', top: windAni[1][1] + 'vw', opacity: 0 }, { left: windAni[1][0] + 'vw', top: windAni[1][1] + 'vw', opacity: 0 },
@ -558,6 +562,7 @@ async function gameEnd() {
easing: 'cubic-bezier(0.4, 0, 1, 0.5)', easing: 'cubic-bezier(0.4, 0, 1, 0.5)',
fill: 'forwards', fill: 'forwards',
}); });
AudioEffects.play("结算")
sunEle.value?.jumpTo('落下'); sunEle.value?.jumpTo('落下');
@ -716,6 +721,7 @@ async function finishCollect() {
document.querySelector('.bar-container').style.display = 'none'; document.querySelector('.bar-container').style.display = 'none';
showLastPage.value = true; showLastPage.value = true;
AudioEffects.play("标题出现")
// Intersection Observer p2 // Intersection Observer p2
await wait(100); // DOM await wait(100); // DOM
@ -726,7 +732,7 @@ async function finishCollect() {
hasP2AnimationPlayed.value = true; hasP2AnimationPlayed.value = true;
posterP2Ele.value?.jumpTo('p2'); posterP2Ele.value?.jumpTo('p2');
console.log('Poster P2 animation triggered'); console.log('Poster P2 animation triggered');
// //
observer.unobserve(entry.target); observer.unobserve(entry.target);
} }
@ -785,6 +791,7 @@ function posterSwap(e: PointerEvent) {
} }
} }
} */ } */
const showShareMask = ref(false);
</script> </script>
<template> <template>
@ -792,14 +799,11 @@ function posterSwap(e: PointerEvent) {
<div class="public"> <div class="public">
<img src="../assets/game/床.webp" alt="" class="abs bed" <img src="../assets/game/床.webp" alt="" class="abs bed" style="width: 66vw;bottom: 12%;left: 17%;">
style="width: 66vw;bottom: 12%;left: 17%;">
<AniEle :url="assets.ani.下拉蓄力提示" ref="shoot-notice" :height="334" <AniEle :url="assets.ani.下拉蓄力提示" ref="shoot-notice" :height="334" :width="337" :rules="[
:width="337" :rules="[ { name: '蓄力', frame: 241, loop: 2 },
{ name: '蓄力', frame: 241, loop: 2 }, ]" class="abs arrow" style="bottom: 7%;width: 30vw;left: 35%;" @pointerdown.once="shoot" />
]" class="abs arrow" style="bottom: 7%;width: 30vw;left: 35%;"
@pointerdown.once="shoot" />
<div class="sun-ani-wrapper abs" v-if="!showEndAni" :style="{ <div class="sun-ani-wrapper abs" v-if="!showEndAni" :style="{
width: '60vw', width: '60vw',
@ -812,13 +816,12 @@ function posterSwap(e: PointerEvent) {
}"> }">
<!-- AniEle 组件现在只关心自己的内容不再有复杂的外部样式 --> <!-- AniEle 组件现在只关心自己的内容不再有复杂的外部样式 -->
<AniEle :url="assets.ani.小太阳总" ref="sun-ani" class="sun-ani" <AniEle :url="assets.ani.小太阳总" ref="sun-ani" class="sun-ani" :height="1800" :width="1600" :rules="[
:height="1800" :width="1600" :rules="[ { name: '起飞前', frame: 90, loop: 0, pauseAfter: true, duration: 33 },
{ name: '起飞前', frame: 90, loop: 0, pauseAfter: true, duration: 33 }, { name: '蓄力飞', frame: 181, loop: 1, pauseAfter: false, duration: 33 },
{ name: '蓄力飞', frame: 181, loop: 1, pauseAfter: false, duration: 33 }, { name: '天上飞', frame: 90, loop: 0, pauseAfter: true, duration: 33 },
{ name: '天上飞', frame: 90, loop: 0, pauseAfter: true, duration: 33 }, { name: '落下', frame: 382, loop: 0, pauseAfter: false, duration: 33 },
{ name: '落下', frame: 382, loop: 0, pauseAfter: false, duration: 33 }, ]" style="width: 100%; height: auto;" />
]" style="width: 100%; height: auto;" />
</div> </div>
</div> </div>
@ -827,59 +830,50 @@ function posterSwap(e: PointerEvent) {
style="bottom: -40%; width: 66vw; left: 17%; transform-origin: 50% 63%; transition: all 0.2s;" style="bottom: -40%; width: 66vw; left: 17%; transform-origin: 50% 63%; transition: all 0.2s;"
:style="{ transform: `rotate(${gDeg}deg)` }" /> :style="{ transform: `rotate(${gDeg}deg)` }" />
<div class="action"> <div class="action">
<img draggable="false" :style="{ animationDelay: '0.1s' }" <img draggable="false" :style="{ animationDelay: '0.1s' }" src="../assets/game/左.webp" alt=""
src="../assets/game/左.webp" alt="" @click="gToggle('left')"> @click="gToggle('left')">
<img draggable="false" :style="{ animationDelay: '0.2s' }" <img draggable="false" :style="{ animationDelay: '0.2s' }" src="../assets/game/中.webp" alt=""
src="../assets/game/中.webp" alt=""
@click="gToggle('center')"> @click="gToggle('center')">
<img draggable="false" :style="{ animationDelay: '0.0s' }" <img draggable="false" :style="{ animationDelay: '0.0s' }" src="../assets/game/右.webp" alt=""
src="../assets/game/右.webp" alt=""
@click="gToggle('right')"> @click="gToggle('right')">
</div> </div>
<img class="abs gift-item" v-for="(gift, index) in giftList" <img class="abs gift-item" v-for="(gift, index) in giftList" :src="gift[0]" style="width: 20vw;"
:src="gift[0]" style="width: 20vw;" v-if="!isGameEnd" :style="{ v-if="!isGameEnd" :style="{
display: giftProgress == index ? 'block' : 'none', display: giftProgress == index ? 'block' : 'none',
bottom: `calc(45% + ${giftPos[1] ?? 0}vw)`, left: `calc(39.5% + ${giftPos[0] ?? 0}vw)`, transition: transitionOn ? `all 0.1s ease` : undefined bottom: `calc(45% + ${giftPos[1] ?? 0}vw)`, left: `calc(39.5% + ${giftPos[0] ?? 0}vw)`, transition: transitionOn ? `all 0.1s ease` : undefined
}" alt="" }" alt="" :class="{ show: giftProgress == index, hide: giftProgress != index }">
:class="{ show: giftProgress == index, hide: giftProgress != index }">
<img src="../assets//game/操作提示.webp" class="abs prompt" alt="" <img src="../assets//game/操作提示.webp" class="abs prompt" alt="" style="width: 31%; bottom: 18%; left: 34%; ">
style="width: 31%; bottom: 18%; left: 34%; ">
<img src="../assets/game/风.webp" alt="" class="abs wind"> <img src="../assets/game/风.webp" alt="" class="abs wind">
<img src="../assets/game/wecare.webp" alt="" class="abs wecare" <img src="../assets/game/wecare.webp" alt="" class="abs wecare" style="width: 24vw;
style="width: 24vw;
left: 50%; top: 6%; transform: translateX(-50%);"> left: 50%; top: 6%; transform: translateX(-50%);">
<div class="dot" v-if="!isGameEnd"> <div class="dot" v-if="!isGameEnd">
<div v-for="(_, index) in giftList" :key="index" <div v-for="(_, index) in giftList" :key="index" class="dot-item"
class="dot-item" :class="{ finished: index < giftProgress }" :class="{ finished: index < giftProgress }" @click="giftProgress = index">
@click="giftProgress = index">
</div> </div>
</div> </div>
</div> </div>
<div class="game-end abs" v-if="isGameEnd" <div class="game-end abs" v-if="isGameEnd"
style="z-index: 2; inset: 0; height: 100%; width: 100%;pointer-events: none;"> style="z-index: 2; inset: 0; height: 100%; width: 100%;pointer-events: none;">
<div class="icon-cloud" <div class="icon-cloud" style="position: absolute; left: 50%; top: 20%;">
style="position: absolute; left: 50%; top: 20%;">
<img v-for="icon in floatIcons" :src="icon.src" alt="" :style="{ <img v-for="icon in floatIcons" :src="icon.src" alt="" :style="{
top: `${icon.top}vw`, top: `${icon.top}vw`,
left: `${icon.left}vw`, left: `${icon.left}vw`,
animationDelay: `${icon.delay}ms`, animationDelay: `${icon.delay}ms`,
}"> }">
</div> </div>
<img src="../assets/game/wecare.webp" alt="" class="abs wecare" <img src="../assets/game/wecare.webp" alt="" class="abs wecare" style="width: 24vw;
style="width: 24vw;
left: 50%; top: 6%; transform: translateX(-50%);"> left: 50%; top: 6%; transform: translateX(-50%);">
<img src="../assets/game/wecare标题.webp" alt="" <img src="../assets/game/wecare标题.webp" alt="" class="abs wecare-title"
class="abs wecare-title" style="width: 72%;left: 14%;top: 12%;"> style="width: 72%;left: 14%;top: 12%;">
<div class="scoreboard" v-if="showScore"> <div class="scoreboard" v-if="showScore">
<div class="bar-container"> <div class="bar-container">
<div class="bar" <div class="bar" v-for="(item, index) in [leaderBoard[1], leaderBoard[0], leaderBoard[2]]"
v-for="(item, index) in [leaderBoard[1], leaderBoard[0], leaderBoard[2]]"
:key="index" :class="{ :key="index" :class="{
'first': index === 1, 'second': index === 0, 'third': index === 2 'first': index === 1, 'second': index === 0, 'third': index === 2
}"> }">
@ -890,9 +884,7 @@ function posterSwap(e: PointerEvent) {
</div> </div>
</div> </div>
<div class="lines"> <div class="lines">
<div class="line" <div class="line" v-for="(item, index) in leaderBoard.slice(3, 7)" :key="index"
v-for="(item, index) in leaderBoard.slice(3, 7)"
:key="index"
:style="{ animationDelay: index * 100 + 400 + 'ms' }"> :style="{ animationDelay: index * 100 + 400 + 'ms' }">
<div class="rank">{{ index + 4 }}</div> <div class="rank">{{ index + 4 }}</div>
<div class="name">{{ item.name }}</div> <div class="name">{{ item.name }}</div>
@ -902,19 +894,16 @@ function posterSwap(e: PointerEvent) {
</div> </div>
</div> </div>
<div class="sun-ani-end-wrapper abs" <div class="sun-ani-end-wrapper abs" style="pointer-events: none; inset: 0; width: 100%;z-index: 2;"
style="pointer-events: none; inset: 0; width: 100%;z-index: 2;"
:style="{ display: showEndAni ? 'block' : 'none' }"> :style="{ display: showEndAni ? 'block' : 'none' }">
<!-- AniEle 组件现在只关心自己的内容不再有复杂的外部样式 --> <!-- AniEle 组件现在只关心自己的内容不再有复杂的外部样式 -->
<AniEle :url="assets.ani.后端效果" ref="sun-ani-end" <AniEle :url="assets.ani.后端效果" ref="sun-ani-end" class="sun-ani-end" :height="2462" :width="1179"
class="sun-ani-end" :height="2462" :width="1179" :rules="sunAniEndRules" style="width: 100%; height: auto;" />
:rules="sunAniEndRules"
style="width: 100%; height: auto;" />
</div> </div>
<img src="../assets/game/完成收集.webp" alt="" v-if="showScore" <img src="../assets/game/完成收集.webp" alt="" v-if="showScore" class="finish-collect abs"
class="finish-collect abs" @click.once="finishCollect" @click.once="AudioEffects.play('按钮音效');finishCollect()"
style="bottom: 8%;left: 33%;width: 34vw;animation: scale-in 0.3s ease-out;pointer-events: all;"> style="bottom: 8%;left: 33%;width: 34vw;animation: scale-in 0.3s ease-out;pointer-events: all;">
@ -924,12 +913,11 @@ function posterSwap(e: PointerEvent) {
style="z-index: 0;position: absolute; inset: 0;height: 100%; width: 100%; pointer-events: none;"> style="z-index: 0;position: absolute; inset: 0;height: 100%; width: 100%; pointer-events: none;">
<AniEle :url="assets.ani.结尾主标" :width="925" :height="340" <AniEle :url="assets.ani.结尾主标" :width="925" :height="340"
:rules="[{ name: 'main', frame: 41, loop: 1, pauseAfter: true, duration: 33, reverse: false }]" :rules="[{ name: 'main', frame: 41, loop: 1, pauseAfter: true, duration: 33, reverse: false }]"
style="position: absolute;top: 13%;width: 80%;left: 11%; pointer-events:none;" style="position: absolute;top: 13%;width: 80%;left: 11%; pointer-events:none;" ref="main-logo" />
ref="main-logo" />
<div class="poster-container" <div class="poster-container"
style="position: absolute;top: 29.3%;left: 37.2%;width: 57.3%; height: 38%; overflow-x: auto; overflow-y: hidden; pointer-events: all; scroll-snap-type: x mandatory;"> style="position: absolute;top: 29.3%;left: 37.2%;width: 57.3%; height: 38%; overflow-x: auto; overflow-y: hidden; pointer-events: all; scroll-snap-type: x mandatory;">
<div class="poster-wrapper" <div class="poster-wrapper" style="display: flex; width: 200%; height: 100%; flex-direction: row;"
style="display: flex; width: 200%; height: 100%; flex-direction: row;" dir="ltr" > dir="ltr">
<AniEle :url="assets.ani.海报p1" ref="gui-ani-end-post-p1 " <AniEle :url="assets.ani.海报p1" ref="gui-ani-end-post-p1 "
style="width: 50%; flex-shrink: 0; scroll-snap-align: start;" class="gui-ani-end-post" style="width: 50%; flex-shrink: 0; scroll-snap-align: start;" class="gui-ani-end-post"
:height="940" :width="671" :rules="[ :height="940" :width="671" :rules="[
@ -943,18 +931,14 @@ function posterSwap(e: PointerEvent) {
]" /> ]" />
</div> </div>
</div> </div>
<AniEle :url="assets.ani.线" ref="gui-ani-end" class="gui-ani-end" <AniEle :url="assets.ani.线" ref="gui-ani-end" class="gui-ani-end" :height="2462" :width="1179" :rules="[
:height="2462" :width="1179" :rules="[ { name: 'all', frame: 54, loop: 1, pauseAfter: false, duration: 16 },
{ name: 'all', frame: 54, loop: 1, pauseAfter: false, duration: 16 }, ]" style="width: 100%; height: auto;position: absolute;inset: 0;pointer-events: none;" />
]" <img src="../assets/game/床.webp" alt="" class="abs last-bed" style="width: 66vw;bottom: -19%;left: 17%;">
style="width: 100%; height: auto;position: absolute;inset: 0;pointer-events: none;" />
<img src="../assets/game/床.webp" alt="" class="abs last-bed"
style="width: 66vw;bottom: -19%;left: 17%;">
<img src="../assets/game/分享海报.webp" alt="" class="abs" <img src="../assets/game/分享海报.webp" alt="" class="abs" @click="AudioEffects.play('按钮音效');showShareMask = true"
style="width: 34vw;bottom: -19%;left: 15%;animation: last-btn-in 0.3s ease-out forwards; cursor: pointer; pointer-events: all;"> style="width: 34vw;bottom: -19%;left: 15%;animation: last-btn-in 0.3s ease-out forwards; cursor: pointer; pointer-events: all;">
<img src="../assets/game/再玩一次.webp" alt="" class="abs" <img src="../assets/game/再玩一次.webp" alt="" class="abs" @click="AudioEffects.play('按钮音效');resetGame()"
@click="resetGame"
style="width: 34vw;bottom: -19%;left: 51%;animation: last-btn-in 0.3s ease-out forwards; cursor: pointer; animation-delay: 200ms; pointer-events: all;"> style="width: 34vw;bottom: -19%;left: 51%;animation: last-btn-in 0.3s ease-out forwards; cursor: pointer; animation-delay: 200ms; pointer-events: all;">
<div class="time-spent abs" style="left: 10%; top:44%;width: 24%;height: 23%; display: flex; <div class="time-spent abs" style="left: 10%; top:44%;width: 24%;height: 23%; display: flex;
@ -976,8 +960,7 @@ function posterSwap(e: PointerEvent) {
style="width: 100%;bottom:7%;position: absolute;animation: line-in 0.5s ease-out 1s forwards;transform: translateX(-210%);"> style="width: 100%;bottom:7%;position: absolute;animation: line-in 0.5s ease-out 1s forwards;transform: translateX(-210%);">
</div> </div>
<div class="region-area abs" <div class="region-area abs" style="left: 10%; top:30%;width: 24%;height: 10%;gap:2vw; display: flex;animation: line-in 0.5s ease-out 0.4s forwards;transform: translateX(-210%);
style="left: 10%; top:30%;width: 24%;height: 10%;gap:2vw; display: flex;animation: line-in 0.5s ease-out 0.4s forwards;transform: translateX(-210%);
align-items: center; flex-direction: column;justify-content: center;"> align-items: center; flex-direction: column;justify-content: center;">
<div class="region" <div class="region"
style="width: 20vw; background-color: white;font-size: 3vw;text-align: center;height: 5.2vw;line-height: 5.2vw; border-radius: 4.5vw;"> style="width: 20vw; background-color: white;font-size: 3vw;text-align: center;height: 5.2vw;line-height: 5.2vw; border-radius: 4.5vw;">
@ -991,18 +974,35 @@ function posterSwap(e: PointerEvent) {
<div v-if="giftShow" class="gift-popup"> <div v-if="giftShow" class="gift-popup">
<img :src="giftList[giftProgress][1]" alt="" class="abs" <img :src="giftList[giftProgress][1]" alt="" class="abs" style="width: 82%; left: 9%; top: 11%;">
style="width: 82%; left: 9%; top: 11%;"> <img src="../assets/game/收下福利.webp" alt="" class="abs" style="width: 14%; left: 43%; top: 54%;"
<img src="../assets/game/收下福利.webp" alt="" class="abs" @click="AudioEffects.play('按钮音效');gameLoop()">
style="width: 14%; left: 43%; top: 54%;" @click="gameLoop()">
</div> </div>
<Transition name="fade">
<div v-if="showShareMask" class="share-mask" @click="showShareMask = false">
<img src="../assets/game/share_mask.png" alt="">
</div>
</Transition>
</div> </div>
</template> </template>
<style lang="scss"> <style lang="scss">
.share-mask {
position: fixed;
inset: 0;
z-index: 1000;
height: 100%;
width: 100%;
img {
height: 100%;
width: 100%;
object-fit: cover;
}
}
.gui-ani-end-post { .gui-ani-end-post {
/* 确保每个海报都能正确对齐 */ /* 确保每个海报都能正确对齐 */
object-fit: contain; object-fit: contain;
@ -1349,11 +1349,14 @@ function posterSwap(e: PointerEvent) {
.poster-container { .poster-container {
/* 隐藏滚动条但保持滚动功能 */ /* 隐藏滚动条但保持滚动功能 */
scrollbar-width: none; /* Firefox */ scrollbar-width: none;
-ms-overflow-style: none; /* IE and Edge */ /* Firefox */
-ms-overflow-style: none;
/* IE and Edge */
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera */ display: none;
/* Chrome, Safari, Opera */
} }
} }

View File

@ -5,6 +5,8 @@ import assets from '../assets';
import { regions, type RegionData, type Store } from '../data'; import { regions, type RegionData, type Store } from '../data';
import AniEle from '../components/AniEle.vue'; import AniEle from '../components/AniEle.vue';
import AudioEffects from '../assets/sounds'
const regionsPos = [ const regionsPos = [
{ w: 68.8, h: 68.8, l: -1, t: 0, src: assets.p1.大北区, tt: '大北区', trt: -13, ad: 6, key: '大北区' }, { w: 68.8, h: 68.8, l: -1, t: 0, src: assets.p1.大北区, tt: '大北区', trt: -13, ad: 6, key: '大北区' },
{ w: 73.3, h: 73.3, l: 44, t: 20, src: assets.p1.大东区, tt: '大东区', ad: 3, key: '大东区' }, { w: 73.3, h: 73.3, l: 44, t: 20, src: assets.p1.大东区, tt: '大东区', ad: 3, key: '大东区' },
@ -101,6 +103,7 @@ const emit = defineEmits<{
const noticeToInputName = ref(false); const noticeToInputName = ref(false);
async function startExploration() { async function startExploration() {
console.log('开始探索:', selectedStores.value[selectedStoreIndex.value]); console.log('开始探索:', selectedStores.value[selectedStoreIndex.value]);
AudioEffects.play("按钮音效");
if (!selectedRegion.value || selectedStores.value.length === 0) { if (!selectedRegion.value || selectedStores.value.length === 0) {
alert('请选择一个区域和店铺!'); alert('请选择一个区域和店铺!');
return; return;
@ -192,6 +195,7 @@ onMounted(() => {
// --- --- // --- ---
const pDown = () => { const pDown = () => {
AudioEffects.play("按钮音效");
if (selectionInProgress.value) return; if (selectionInProgress.value) return;
bubble.style.filter = 'brightness(0.9)'; bubble.style.filter = 'brightness(0.9)';
bubble.style.transform = 'translate3d(-50%,-50%, 0) scale(0.92)'; bubble.style.transform = 'translate3d(-50%,-50%, 0) scale(0.92)';
@ -257,10 +261,12 @@ onMounted(() => {
function goBackToRegionSelection() { function goBackToRegionSelection() {
AudioEffects.play("按钮音效");
if (!selectedBubbleEl.value) return; if (!selectedBubbleEl.value) return;
selectionInProgress.value = true; // selectionInProgress.value = true; //
mainLogoRules.value[0].reverse = false; mainLogoRules.value[0].reverse = false;
AudioEffects.play("标题出现")
selectInfoRules.value[0].reverse = false; selectInfoRules.value[0].reverse = false;
// 1. // 1.