59 changed files with 2758 additions and 1743 deletions
@ -1,147 +1,263 @@ |
|||
<template> |
|||
<view> |
|||
<swiper class="swiper" :current="currentIndex" :vertical="true" @change="handleSwiperChange"> |
|||
<swiper-item v-for="(item, index) in swiperItems" :key="index" v-if="index < indexShow"> |
|||
<view :class="['swiper-item',{'swiper-item1-10': item.images}]" :style="{ backgroundImage: `url(${item.imageUrl})` }" @click="changeIndex(index)"> |
|||
<!-- 如果是第一章的第10个swiper-item,显示图片并绑定点击事件 --> |
|||
<template v-if="item.images"> |
|||
<image v-for="(image, imgIndex) in item.images" :key="imgIndex" :src="image.src" |
|||
mode="aspectFill" @click="setStorage(imgIndex);gotoPath(item.link.replace('{index}', imgIndex + item.linkIndex))"></image> |
|||
</template> |
|||
<view> |
|||
<swiper class="swiper" :current="currentIndex" :vertical="true" @change="handleSwiperChange"> |
|||
<swiper-item v-for="(item, index) in swiperItems" :key="index" v-if="index < indexShow"> |
|||
<view :class="['swiper-item',{'swiper-item1-10': item.images}]" |
|||
:style="{ backgroundImage: `url(${item.imageUrl})` }" @click="changeIndex(index)"> |
|||
<!-- 如果是第一章的第10个swiper-item,显示图片并绑定点击事件 --> |
|||
<template v-if="item.images"> |
|||
<image v-for="(image, imgIndex) in item.images" :key="imgIndex" :src="image.src" |
|||
mode="aspectFill" |
|||
@click="setStorage(imgIndex);gotoPath(item.link.replace('{index}', imgIndex + item.linkIndex))"> |
|||
</image> |
|||
</template> |
|||
<!-- 视频 --> |
|||
<template v-if="index == 3"> |
|||
<video src="https://static.ticket.sz-trip.com/epicSoul/bmzm.mp4" style="width: 100vw;height: 30vh;" objectFit="cover"></video> |
|||
<video src="https://static.ticket.sz-trip.com/epicSoul/bmzm.mp4" @play="handleVideoPlay" |
|||
@pause="handleVideoPause" @ended="handleVideoEnded" style="width: 100vw;height: 30vh;" |
|||
objectFit="cover"></video> |
|||
</template> |
|||
<!-- 动图 --> |
|||
<template v-if="index == 8"> |
|||
<image src="https://static.ticket.sz-trip.com/epicSoul/bmzm/chapter1/img7s.gif" mode="widthFix" style="width: 100vw;"></image> |
|||
<image src="https://static.ticket.sz-trip.com/epicSoul/bmzm/chapter1/img7s.gif" mode="widthFix" |
|||
style="width: 100vw;"></image> |
|||
</template> |
|||
</view> |
|||
</swiper-item> |
|||
</swiper> |
|||
</view> |
|||
</swiper-item> |
|||
</swiper> |
|||
<MusicControl /> |
|||
<NavMenu :nav-index="0" @jump-to-page="handleJumpToPage" /> |
|||
</view> |
|||
<AudioControl audioSrc="https://des.js-dyyj.com/data/2025/09/05/9875a62d-14ef-481e-b88f-19c061478ce6.MP3" /> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import MusicControl from '@/components/MusicControl.vue'; |
|||
import NavMenu from '../components/NavMenu.vue'; |
|||
export default { |
|||
components: { |
|||
MusicControl, |
|||
NavMenu |
|||
}, |
|||
data() { |
|||
return { |
|||
currentIndex: 0, |
|||
// 禁止滑动到的索引 |
|||
forbiddenIndex: 2, |
|||
initialIndex: 1, |
|||
popupIndex: 1, |
|||
// 抽象出swiper-item的数据 |
|||
swiperItems: [ |
|||
{ |
|||
imageUrl: `https://static.ticket.sz-trip.com/epicSoul/bmzm/index/index1.png` |
|||
}, |
|||
{ |
|||
imageUrl: 'https://static.ticket.sz-trip.com/epicSoul/bmzm/index/index5.png' |
|||
}, |
|||
// 第一章 |
|||
...Array.from({ length: 9 }, (_, i) => ({ |
|||
imageUrl: `https://static.ticket.sz-trip.com/epicSoul/bmzm/chapter1/img${i + 1}.png` |
|||
})), |
|||
{ |
|||
imageUrl: 'https://static.ticket.sz-trip.com/epicSoul/bmzm/chapter1/img10.png', |
|||
images: Array.from({ length: 4 }, (_, i) => ({ |
|||
src: `https://static.ticket.sz-trip.com/epicSoul/bmzm/chapter1/img10-${i + 1}.png` |
|||
})), |
|||
link: '/bmzm/chapter2/index?index={index}', |
|||
linkIndex: 11 |
|||
} |
|||
], |
|||
indexShow: 3 |
|||
}; |
|||
}, |
|||
onLoad(option) { |
|||
this.initialIndex = option.index; |
|||
// 更新第一个swiper-item的图片路径 |
|||
this.swiperItems[0].imageUrl = `https://static.ticket.sz-trip.com/epicSoul/bmzm/index/index${this.initialIndex}.png`; |
|||
}, |
|||
methods: { |
|||
touchmove() { |
|||
return this.currentIndex == 2 ? true : false |
|||
import AudioControl from '@/components/AudioControl.vue'; |
|||
import MusicControl from '@/components/MusicControl.vue'; |
|||
import NavMenu from '../components/NavMenu.vue'; |
|||
export default { |
|||
components: { |
|||
MusicControl, |
|||
NavMenu, |
|||
AudioControl |
|||
}, |
|||
data() { |
|||
return { |
|||
currentIndex: 0, |
|||
// 禁止滑动到的索引 |
|||
forbiddenIndex: 2, |
|||
initialIndex: 1, |
|||
popupIndex: 1, |
|||
// 视频播放前的状态记录 |
|||
beforeVideoState: { |
|||
audioWasPlaying: false, |
|||
bgMusicWasPlaying: false |
|||
}, |
|||
// 抽象出swiper-item的数据 |
|||
swiperItems: [{ |
|||
imageUrl: `https://static.ticket.sz-trip.com/epicSoul/bmzm/index/index1.png` |
|||
}, |
|||
{ |
|||
imageUrl: 'https://static.ticket.sz-trip.com/epicSoul/bmzm/index/index5.png' |
|||
}, |
|||
// 第一章 |
|||
...Array.from({ |
|||
length: 9 |
|||
}, (_, i) => ({ |
|||
imageUrl: `https://static.ticket.sz-trip.com/epicSoul/bmzm/chapter1/img${i + 1}.png` |
|||
})), |
|||
{ |
|||
imageUrl: 'https://static.ticket.sz-trip.com/epicSoul/bmzm/chapter1/img10.png', |
|||
images: Array.from({ |
|||
length: 4 |
|||
}, (_, i) => ({ |
|||
src: `https://static.ticket.sz-trip.com/epicSoul/bmzm/chapter1/img10-${i + 1}.png` |
|||
})), |
|||
link: '/bmzm/chapter2/index?index={index}', |
|||
linkIndex: 11 |
|||
} |
|||
], |
|||
indexShow: 3 |
|||
}; |
|||
}, |
|||
changeIndex(index) { |
|||
if(index == 2) this.currentIndex = 3 |
|||
this.indexShow = 100 |
|||
onLoad(option) { |
|||
this.initialIndex = option.index; |
|||
// 更新第一个swiper-item的图片路径 |
|||
this.swiperItems[0].imageUrl = |
|||
`https://static.ticket.sz-trip.com/epicSoul/bmzm/index/index${this.initialIndex}.png`; |
|||
}, |
|||
handleSwiperChange(e) { |
|||
this.currentIndex = e.detail.current; |
|||
if(this.currentIndex == 2) this.indexShow = 3 |
|||
}, |
|||
// 存储答案,供后面使用 |
|||
setStorage(i) { |
|||
let text = '' |
|||
switch (i){ |
|||
case 0: |
|||
text = '灵感的捕捉者' |
|||
break; |
|||
case 1: |
|||
text = '众智的编织者' |
|||
break; |
|||
case 2: |
|||
text = '智慧的开启者' |
|||
break; |
|||
case 3: |
|||
text = '自然的尊重者' |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
this.appendToStorage('answerObj', { answer2: text }); |
|||
methods: { |
|||
touchmove() { |
|||
return this.currentIndex == 2 ? true : false |
|||
}, |
|||
changeIndex(index) { |
|||
if (index == 2) this.currentIndex = 3 |
|||
this.indexShow = 100 |
|||
}, |
|||
handleSwiperChange(e) { |
|||
this.currentIndex = e.detail.current; |
|||
if (this.currentIndex == 2) this.indexShow = 3 |
|||
}, |
|||
// 存储答案,供后面使用 |
|||
setStorage(i) { |
|||
let text = '' |
|||
switch (i) { |
|||
case 0: |
|||
text = '灵感的捕捉者' |
|||
break; |
|||
case 1: |
|||
text = '众智的编织者' |
|||
break; |
|||
case 2: |
|||
text = '智慧的开启者' |
|||
break; |
|||
case 3: |
|||
text = '自然的尊重者' |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
this.appendToStorage('answerObj', { |
|||
answer2: text |
|||
}); |
|||
}, |
|||
// 视频开始播放 |
|||
handleVideoPlay() { |
|||
console.log('视频开始播放'); |
|||
|
|||
// 记录当前状态 |
|||
this.recordCurrentState(); |
|||
|
|||
// 暂停音频和背景音乐 |
|||
this.pauseAllAudio(); |
|||
}, |
|||
|
|||
// 视频暂停 |
|||
handleVideoPause() { |
|||
console.log('视频暂停'); |
|||
// 恢复之前的状态 |
|||
this.restorePreviousState(); |
|||
}, |
|||
|
|||
// 视频播放结束 |
|||
handleVideoEnded() { |
|||
console.log('视频播放结束'); |
|||
// 恢复之前的状态 |
|||
this.restorePreviousState(); |
|||
}, |
|||
|
|||
// 记录当前音频和背景音乐状态 |
|||
recordCurrentState() { |
|||
try { |
|||
// 检查音频状态 |
|||
if (uni.$globalAudio && uni.$globalAudio.isAudioPlaying()) { |
|||
this.beforeVideoState.audioWasPlaying = true; |
|||
console.log('bmzm chapter1: 记录:音频正在播放'); |
|||
} else { |
|||
this.beforeVideoState.audioWasPlaying = false; |
|||
} |
|||
|
|||
// 检查背景音乐状态 |
|||
const app = getApp(); |
|||
if (app && app.globalData && app.globalData.isMusicPlaying) { |
|||
this.beforeVideoState.bgMusicWasPlaying = true; |
|||
console.log('bmzm chapter1: 记录:背景音乐正在播放'); |
|||
} else { |
|||
this.beforeVideoState.bgMusicWasPlaying = false; |
|||
} |
|||
} catch (error) { |
|||
console.error('bmzm chapter1: 记录状态失败:', error); |
|||
} |
|||
}, |
|||
|
|||
// 暂停所有音频 |
|||
pauseAllAudio() { |
|||
try { |
|||
// 暂停音频 |
|||
if (this.beforeVideoState.audioWasPlaying && uni.$globalAudio) { |
|||
uni.$globalAudio.pauseCurrentAudio(); |
|||
console.log('bmzm chapter1: 暂停音频'); |
|||
} |
|||
|
|||
// 暂停背景音乐 |
|||
if (this.beforeVideoState.bgMusicWasPlaying) { |
|||
const app = getApp(); |
|||
if (app && app.globalData && app.globalData.bgMusic) { |
|||
app.globalData.bgMusic.pause(); |
|||
console.log('bmzm chapter1: 暂停背景音乐'); |
|||
} |
|||
} |
|||
} catch (error) { |
|||
console.error('bmzm chapter1: 暂停音频失败:', error); |
|||
} |
|||
}, |
|||
|
|||
// 恢复之前的状态 |
|||
restorePreviousState() { |
|||
try { |
|||
// 恢复音频 |
|||
if (this.beforeVideoState.audioWasPlaying && uni.$globalAudio) { |
|||
uni.$globalAudio.playCurrentAudio(); |
|||
console.log('bmzm chapter1: 恢复音频播放'); |
|||
} |
|||
|
|||
// 恢复背景音乐 |
|||
if (this.beforeVideoState.bgMusicWasPlaying) { |
|||
const app = getApp(); |
|||
if (app && app.globalData && app.globalData.bgMusic) { |
|||
app.globalData.bgMusic.play(); |
|||
console.log('bmzm chapter1: 恢复背景音乐播放'); |
|||
} |
|||
} |
|||
|
|||
// 重置状态记录 |
|||
this.beforeVideoState.audioWasPlaying = false; |
|||
this.beforeVideoState.bgMusicWasPlaying = false; |
|||
} catch (error) { |
|||
console.error('bmzm chapter1: 恢复状态失败:', error); |
|||
} |
|||
}, |
|||
} |
|||
} |
|||
}; |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.swiper { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
} |
|||
|
|||
.swiper-item { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
.swiper-item1-10 { |
|||
padding: 506rpx 65rpx 370rpx; |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
justify-content: space-between; |
|||
|
|||
&>image:nth-child(n+1) { |
|||
width: 310rpx; |
|||
height: 316.58rpx; |
|||
} |
|||
|
|||
&>image:nth-child(n+2) { |
|||
width: 291.61rpx; |
|||
height: 334.55rpx; |
|||
} |
|||
|
|||
&>image:nth-child(n+3) { |
|||
width: 292.61rpx; |
|||
height: 334.55rpx; |
|||
} |
|||
|
|||
&>image:nth-child(n+4) { |
|||
width: 309.59rpx; |
|||
height: 317.58rpx; |
|||
margin-top: 15rpx; |
|||
} |
|||
} |
|||
.swiper { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
} |
|||
|
|||
.swiper-item { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
background-size: 100% 100%; |
|||
} |
|||
|
|||
.swiper-item1-10 { |
|||
padding: 506rpx 65rpx 370rpx; |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
justify-content: space-between; |
|||
|
|||
&>image:nth-child(n+1) { |
|||
width: 310rpx; |
|||
height: 316.58rpx; |
|||
} |
|||
|
|||
&>image:nth-child(n+2) { |
|||
width: 291.61rpx; |
|||
height: 334.55rpx; |
|||
} |
|||
|
|||
&>image:nth-child(n+3) { |
|||
width: 292.61rpx; |
|||
height: 334.55rpx; |
|||
} |
|||
|
|||
&>image:nth-child(n+4) { |
|||
width: 309.59rpx; |
|||
height: 317.58rpx; |
|||
margin-top: 15rpx; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,289 @@ |
|||
<template> |
|||
<view class="audio-control" :style="{top:top}" @click.stop="toggleAudio"> |
|||
<image v-if="isAudioPlaying" :src="showImg('/uploads/20250904/c7ddc330d25a0916885a31f1d28f6665.png')" mode="" style="width: 80rpx;height: 80rpx;"></image> |
|||
<image v-else :src="showImg('/uploads/20250904/057669bd710740650db1cafd414c08c9.png')" mode="" style="width: 80rpx;height: 80rpx;"></image> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'AudioControl', |
|||
inheritAttrs: false, |
|||
props: { |
|||
// 音频文件路径 |
|||
audioSrc: { |
|||
type: String, |
|||
required: true |
|||
}, |
|||
top:{ |
|||
type:String, |
|||
default:'180rpx' |
|||
}, |
|||
// 是否显示组件 |
|||
visible: { |
|||
type: Boolean, |
|||
default: true |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
isAudioPlaying: false, |
|||
audioContext: null, |
|||
backgroundMusicWasPlaying: false, // 记录背景音乐之前的状态 |
|||
audioInstanceId: null // 音频实例标识 |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.initAudio(); |
|||
|
|||
// 监听背景音乐切换事件 |
|||
uni.$on('backgroundMusicToggle', this.handleBackgroundMusicToggle); |
|||
|
|||
// 页面加载时检查是否有全局音频在播放 |
|||
this.checkGlobalAudioState(); |
|||
}, |
|||
beforeUnmount() { |
|||
this.destroyAudio(); |
|||
|
|||
// 移除事件监听 |
|||
uni.$off('backgroundMusicToggle', this.handleBackgroundMusicToggle); |
|||
}, |
|||
methods: { |
|||
initAudio() { |
|||
try { |
|||
// 检查是否已有全局音频实例 |
|||
const app = getApp(); |
|||
if (app && app.globalData && app.globalData.currentAudio) { |
|||
// 如果有全局音频且URL匹配,则复用 |
|||
if (app.globalData.currentAudio.src === this.audioSrc) { |
|||
this.audioContext = app.globalData.currentAudio; |
|||
this.isAudioPlaying = !this.audioContext.paused; |
|||
this.setupAudioEvents(); |
|||
return; |
|||
} else { |
|||
// 如果URL不匹配,停止之前的音频 |
|||
app.globalData.currentAudio.stop(); |
|||
app.globalData.currentAudio.destroy(); |
|||
} |
|||
} |
|||
|
|||
// 创建新的音频上下文 |
|||
this.audioContext = uni.createInnerAudioContext(); |
|||
this.audioContext.src = this.audioSrc; |
|||
|
|||
// 保存到全局 |
|||
if (app && app.globalData) { |
|||
app.globalData.currentAudio = this.audioContext; |
|||
} |
|||
|
|||
this.setupAudioEvents(); |
|||
|
|||
} catch (error) { |
|||
console.error('初始化音频失败:', error); |
|||
} |
|||
}, |
|||
|
|||
setupAudioEvents() { |
|||
if (!this.audioContext) return; |
|||
|
|||
// 监听音频事件 |
|||
this.audioContext.onPlay(() => { |
|||
this.isAudioPlaying = true; |
|||
console.log('音频开始播放'); |
|||
}); |
|||
|
|||
this.audioContext.onPause(() => { |
|||
this.isAudioPlaying = false; |
|||
console.log('音频暂停'); |
|||
}); |
|||
|
|||
this.audioContext.onStop(() => { |
|||
this.isAudioPlaying = false; |
|||
console.log('音频停止'); |
|||
}); |
|||
|
|||
this.audioContext.onEnded(() => { |
|||
this.isAudioPlaying = false; |
|||
// 音频播放结束后恢复背景音乐 |
|||
this.restoreBackgroundMusic(); |
|||
console.log('音频播放结束'); |
|||
}); |
|||
|
|||
this.audioContext.onError((err) => { |
|||
console.error('音频播放错误:', err); |
|||
this.isAudioPlaying = false; |
|||
// 发生错误时也恢复背景音乐 |
|||
this.restoreBackgroundMusic(); |
|||
}); |
|||
}, |
|||
|
|||
checkGlobalAudioState() { |
|||
try { |
|||
const app = getApp(); |
|||
if (app && app.globalData && app.globalData.currentAudio) { |
|||
const globalAudio = app.globalData.currentAudio; |
|||
// 如果全局音频的URL与当前组件的URL匹配 |
|||
if (globalAudio.src === this.audioSrc) { |
|||
this.isAudioPlaying = !globalAudio.paused; |
|||
console.log('同步全局音频状态:', this.isAudioPlaying); |
|||
} |
|||
} |
|||
} catch (error) { |
|||
console.error('检查全局音频状态失败:', error); |
|||
} |
|||
}, |
|||
|
|||
toggleAudio() { |
|||
if (!this.audioContext) { |
|||
console.error('音频上下文未初始化'); |
|||
return; |
|||
} |
|||
|
|||
if (this.isAudioPlaying) { |
|||
// 暂停音频,恢复背景音乐 |
|||
this.pauseAudio(); |
|||
} else { |
|||
// 播放音频,暂停背景音乐 |
|||
this.playAudio(); |
|||
} |
|||
}, |
|||
|
|||
playAudio() { |
|||
try { |
|||
// 记录背景音乐当前状态并暂停 |
|||
this.pauseBackgroundMusic(); |
|||
|
|||
// 播放音频 |
|||
this.audioContext.play(); |
|||
|
|||
// 发送音频播放事件 |
|||
uni.$emit('audioPlaying', true); |
|||
} catch (error) { |
|||
console.error('播放音频失败:', error); |
|||
} |
|||
}, |
|||
|
|||
pauseAudio() { |
|||
try { |
|||
// 暂停音频 |
|||
this.audioContext.pause(); |
|||
|
|||
// 恢复背景音乐 |
|||
this.restoreBackgroundMusic(); |
|||
|
|||
// 发送音频停止事件 |
|||
uni.$emit('audioPlaying', false); |
|||
} catch (error) { |
|||
console.error('暂停音频失败:', error); |
|||
} |
|||
}, |
|||
|
|||
pauseBackgroundMusic() { |
|||
try { |
|||
const app = getApp(); |
|||
if (app && app.globalData && app.globalData.bgMusic) { |
|||
// 记录背景音乐当前状态 |
|||
this.backgroundMusicWasPlaying = app.globalData.isMusicPlaying; |
|||
|
|||
// 如果背景音乐正在播放,则暂停 |
|||
if (this.backgroundMusicWasPlaying) { |
|||
app.globalData.bgMusic.pause(); |
|||
console.log('背景音乐已暂停'); |
|||
} |
|||
} |
|||
} catch (error) { |
|||
console.error('暂停背景音乐失败:', error); |
|||
} |
|||
}, |
|||
|
|||
restoreBackgroundMusic() { |
|||
try { |
|||
const app = getApp(); |
|||
if (app && app.globalData && app.globalData.bgMusic && this.backgroundMusicWasPlaying) { |
|||
// 恢复背景音乐播放 |
|||
app.globalData.bgMusic.play(); |
|||
console.log('背景音乐已恢复'); |
|||
} |
|||
} catch (error) { |
|||
console.error('恢复背景音乐失败:', error); |
|||
} |
|||
}, |
|||
|
|||
destroyAudio() { |
|||
// 注意:不销毁全局音频实例,只是断开当前组件的连接 |
|||
console.log('AudioControl组件销毁,但保持全局音频实例'); |
|||
// 清空当前组件的引用,但不销毁全局音频 |
|||
this.audioContext = null; |
|||
}, |
|||
|
|||
// 处理背景音乐切换事件 |
|||
handleBackgroundMusicToggle() { |
|||
// 如果音频正在播放,则暂停音频(但不恢复背景音乐) |
|||
if (this.isAudioPlaying) { |
|||
try { |
|||
// 只暂停音频,不恢复背景音乐 |
|||
this.audioContext.pause(); |
|||
|
|||
// 发送音频停止事件 |
|||
uni.$emit('audioPlaying', false); |
|||
|
|||
console.log('背景音乐切换时暂停音频(不恢复背景音乐)'); |
|||
} catch (error) { |
|||
console.error('暂停音频失败:', error); |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
watch: { |
|||
// 监听音频源变化 |
|||
audioSrc: { |
|||
handler(newSrc) { |
|||
if (this.audioContext && newSrc) { |
|||
this.audioContext.src = newSrc; |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.audio-control { |
|||
position: absolute; |
|||
top: 180rpx; |
|||
right: 30rpx; |
|||
width: 80rpx; |
|||
height: 80rpx; |
|||
border-radius: 50%; |
|||
// background-color: rgba(0, 0, 0, 0.5); |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
z-index: 998; |
|||
transition: all 0.3s ease; |
|||
|
|||
&:active { |
|||
transform: scale(0.95); |
|||
} |
|||
} |
|||
|
|||
.audio-icon { |
|||
font-size: 36rpx; |
|||
color: #fff; |
|||
transition: all 0.3s ease; |
|||
} |
|||
|
|||
.playing { |
|||
animation: pulse 1.5s ease-in-out infinite; |
|||
} |
|||
|
|||
@keyframes pulse { |
|||
0%, 100% { |
|||
transform: scale(1); |
|||
} |
|||
50% { |
|||
transform: scale(1.1); |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,155 @@ |
|||
# AudioControl 音频控制组件使用文档 |
|||
|
|||
## 组件功能 |
|||
- 在父组件右上角显示音频控制图标 |
|||
- 点击图标播放指定音频,同时暂停背景音乐 |
|||
- 再次点击暂停音频,恢复背景音乐 |
|||
- 音频播放结束后自动恢复背景音乐 |
|||
|
|||
## 组件属性 (Props) |
|||
|
|||
| 属性名 | 类型 | 必填 | 默认值 | 说明 | |
|||
|--------|------|------|--------|------| |
|||
| audioSrc | String | 是 | - | 音频文件路径 | |
|||
| visible | Boolean | 否 | true | 是否显示组件 | |
|||
|
|||
## 使用方法 |
|||
|
|||
### 1. 在父组件中引入和注册组件 |
|||
|
|||
```vue |
|||
<template> |
|||
<view class="parent-container"> |
|||
<!-- 父组件内容 --> |
|||
<view class="content"> |
|||
<!-- 你的页面内容 --> |
|||
</view> |
|||
|
|||
<!-- 音频控制组件 --> |
|||
<AudioControl |
|||
:audioSrc="audioUrl" |
|||
:visible="showAudio" |
|||
/> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import AudioControl from '@/components/AudioControl.vue'; |
|||
|
|||
export default { |
|||
components: { |
|||
AudioControl |
|||
}, |
|||
data() { |
|||
return { |
|||
audioUrl: 'https://your-domain.com/audio/sample.mp3', // 替换为你的音频URL |
|||
showAudio: true |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.parent-container { |
|||
position: relative; /* 重要:确保AudioControl能正确定位 */ |
|||
width: 100vw; |
|||
height: 100vh; |
|||
} |
|||
</style> |
|||
``` |
|||
|
|||
### 2. 使用项目中的showImg方法(推荐) |
|||
|
|||
如果你的音频文件也存储在项目服务器上,可以使用项目的showImg方法: |
|||
|
|||
```vue |
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
audioUrl: this.showImg('/uploads/audio/your-audio-file.mp3'), |
|||
showAudio: true |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
``` |
|||
|
|||
### 3. 动态控制音频源 |
|||
|
|||
你可以根据不同的页面或条件播放不同的音频: |
|||
|
|||
```vue |
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
audioUrl: '', |
|||
showAudio: true |
|||
} |
|||
}, |
|||
mounted() { |
|||
// 根据页面设置不同的音频 |
|||
this.setAudioForCurrentPage(); |
|||
}, |
|||
methods: { |
|||
setAudioForCurrentPage() { |
|||
const currentRoute = this.$route.path; // 假设使用vue-router |
|||
|
|||
switch(currentRoute) { |
|||
case '/chapter1': |
|||
this.audioUrl = this.showImg('/uploads/audio/chapter1.mp3'); |
|||
break; |
|||
case '/chapter2': |
|||
this.audioUrl = this.showImg('/uploads/audio/chapter2.mp3'); |
|||
break; |
|||
default: |
|||
this.audioUrl = this.showImg('/uploads/audio/default.mp3'); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
``` |
|||
|
|||
## 样式说明 |
|||
|
|||
组件默认定位在父组件的右上角(top: 30rpx, right: 30rpx),如果需要调整位置,可以在父组件中覆盖样式: |
|||
|
|||
```vue |
|||
<style> |
|||
/* 调整音频控制组件位置 */ |
|||
.parent-container ::v-deep .audio-control { |
|||
top: 50rpx !important; |
|||
right: 50rpx !important; |
|||
} |
|||
</style> |
|||
``` |
|||
|
|||
## 注意事项 |
|||
|
|||
1. **父组件样式**:确保父组件设置了 `position: relative`,这样AudioControl组件才能正确定位 |
|||
2. **音频格式**:建议使用 mp3 格式的音频文件,兼容性最好 |
|||
3. **音频路径**:确保音频文件路径正确且可访问 |
|||
4. **背景音乐**:组件会自动处理与MusicControl组件的交互,无需额外配置 |
|||
|
|||
## 图标说明 |
|||
|
|||
- 🔊:音频未播放状态 |
|||
- 🎧:音频播放中状态,带有脉动动画效果 |
|||
|
|||
## 事件处理 |
|||
|
|||
组件内部已处理所有音频播放逻辑,包括: |
|||
- 播放音频时自动暂停背景音乐 |
|||
- 暂停音频时自动恢复背景音乐 |
|||
- 音频播放结束时自动恢复背景音乐 |
|||
- 组件销毁时自动清理资源 |
|||
|
|||
## 示例场景 |
|||
|
|||
适用于以下场景: |
|||
- 章节页面播放对应的音频解说 |
|||
- 展示页面播放介绍音频 |
|||
- 互动页面播放提示音频 |
|||
- 任何需要临时播放音频并暂停背景音乐的场景 |
@ -0,0 +1,225 @@ |
|||
# 跨页面音频控制解决方案 |
|||
|
|||
## 进一步优化:解决跨页面状态同步问题 |
|||
|
|||
### 问题描述 |
|||
在跨页面场景下,当音频正在播放时跳转到新页面,新页面的MusicControl组件不知道有音频在播放,点击背景音乐按钮时会直接播放背景音乐,导致音频和背景音乐同时播放。 |
|||
|
|||
### 解决方案 |
|||
|
|||
#### 1. MusicControl组件增强检测 |
|||
```javascript |
|||
// 在mounted生命周期中添加全局音频状态检测 |
|||
mounted() { |
|||
this.syncMusicState(); |
|||
this.checkGlobalAudioState(); // 新增:检查全局音频状态 |
|||
|
|||
// 定时器也要检查全局音频状态 |
|||
this.timer = setInterval(() => { |
|||
this.syncMusicState(); |
|||
this.checkGlobalAudioState(); |
|||
}, 1000); |
|||
} |
|||
|
|||
// 新增方法:检查全局音频状态 |
|||
checkGlobalAudioState() { |
|||
const app = getApp(); |
|||
if (app && app.globalData && app.globalData.currentAudio) { |
|||
const globalAudio = app.globalData.currentAudio; |
|||
this.isAudioPlaying = !globalAudio.paused; |
|||
} else { |
|||
this.isAudioPlaying = false; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### 2. 全局音频管理工具优化 |
|||
```javascript |
|||
// 在音频状态变化时发送全局事件 |
|||
pauseCurrentAudio() { |
|||
const audio = this.getCurrentAudio(); |
|||
if (audio && !audio.paused) { |
|||
audio.pause(); |
|||
this.notifyAudioStateChange(false); // 通知状态变化 |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
// 新增:通知音频状态变化 |
|||
notifyAudioStateChange(isPlaying) { |
|||
if (typeof uni !== 'undefined') { |
|||
uni.$emit('audioPlaying', isPlaying); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### 修复后的交互流程 |
|||
```mermaid |
|||
graph TD |
|||
A[跨页面跳转] --> B[MusicControl组件加载] |
|||
B --> C[检查全局音频状态] |
|||
C --> D{有音频在播放?} |
|||
D -->|是| E[设置isAudioPlaying=true] |
|||
D -->|否| F[设置isAudioPlaying=false] |
|||
E --> G[点击背景音乐按钮] |
|||
F --> G |
|||
G --> H{检查isAudioPlaying} |
|||
H -->|有音频| I[先暂停音频再播放背景音乐] |
|||
H -->|无音频| J[直接播放背景音乐] |
|||
``` |
|||
|
|||
## 问题描述 |
|||
|
|||
AudioControl组件在页面跳转时会出现以下问题: |
|||
1. 组件状态重置,图标显示不正确 |
|||
2. 音频实例丢失连接,但音频可能仍在播放 |
|||
3. 无法在其他页面控制正在播放的音频 |
|||
|
|||
## 解决方案 |
|||
|
|||
### 1. 全局音频实例管理 |
|||
|
|||
在`App.vue`的`globalData`中添加`currentAudio`属性,用于保存当前的音频实例: |
|||
|
|||
```javascript |
|||
globalData: { |
|||
// ... 其他属性 |
|||
currentAudio: null // 全局音频实例 |
|||
} |
|||
``` |
|||
|
|||
### 2. AudioControl组件优化 |
|||
|
|||
#### 状态同步机制 |
|||
- 组件挂载时检查全局音频状态 |
|||
- 复用已存在的音频实例(如果URL匹配) |
|||
- 组件销毁时不销毁全局音频实例 |
|||
|
|||
#### 核心方法改进 |
|||
```javascript |
|||
// 检查全局音频状态 |
|||
checkGlobalAudioState() { |
|||
const app = getApp(); |
|||
if (app && app.globalData && app.globalData.currentAudio) { |
|||
const globalAudio = app.globalData.currentAudio; |
|||
if (globalAudio.src === this.audioSrc) { |
|||
this.isAudioPlaying = !globalAudio.paused; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 初始化音频时复用全局实例 |
|||
initAudio() { |
|||
const app = getApp(); |
|||
if (app && app.globalData && app.globalData.currentAudio) { |
|||
if (app.globalData.currentAudio.src === this.audioSrc) { |
|||
// 复用现有实例 |
|||
this.audioContext = app.globalData.currentAudio; |
|||
this.isAudioPlaying = !this.audioContext.paused; |
|||
return; |
|||
} |
|||
} |
|||
// 创建新实例... |
|||
} |
|||
``` |
|||
|
|||
### 3. 全局音频管理工具 |
|||
|
|||
创建了`utils/globalAudioManager.js`工具类,提供统一的音频控制接口: |
|||
|
|||
```javascript |
|||
// 在任何页面或组件中使用 |
|||
uni.$globalAudio.pauseCurrentAudio(); // 暂停当前音频 |
|||
uni.$globalAudio.playCurrentAudio(); // 播放当前音频 |
|||
uni.$globalAudio.isAudioPlaying(); // 检查播放状态 |
|||
uni.$globalAudio.getCurrentAudioSrc(); // 获取当前音频源 |
|||
``` |
|||
|
|||
## 使用示例 |
|||
|
|||
### 在页面中控制音频 |
|||
|
|||
```vue |
|||
<template> |
|||
<view> |
|||
<!-- 音频控制组件 --> |
|||
<AudioControl :audioSrc="audioUrl" /> |
|||
|
|||
<!-- 手动控制按钮 --> |
|||
<button @click="toggleAudio">切换音频</button> |
|||
<button @click="stopAudio">停止音频</button> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import AudioControl from '@/components/AudioControl.vue'; |
|||
|
|||
export default { |
|||
components: { AudioControl }, |
|||
data() { |
|||
return { |
|||
audioUrl: this.showImg('/uploads/audio/chapter1.mp3') |
|||
} |
|||
}, |
|||
methods: { |
|||
toggleAudio() { |
|||
if (uni.$globalAudio.isAudioPlaying()) { |
|||
uni.$globalAudio.pauseCurrentAudio(); |
|||
} else { |
|||
uni.$globalAudio.playCurrentAudio(); |
|||
} |
|||
}, |
|||
|
|||
stopAudio() { |
|||
uni.$globalAudio.stopCurrentAudio(); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
``` |
|||
|
|||
### 在其他页面检查音频状态 |
|||
|
|||
```javascript |
|||
// 在任何页面的onShow生命周期中 |
|||
onShow() { |
|||
// 检查是否有音频在播放 |
|||
if (uni.$globalAudio.isAudioPlaying()) { |
|||
console.log('有音频正在播放:', uni.$globalAudio.getCurrentAudioSrc()); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## 技术优势 |
|||
|
|||
### ✅ **状态持久化** |
|||
- 音频实例在页面跳转时不会丢失 |
|||
- 组件状态能够正确同步全局音频状态 |
|||
|
|||
### ✅ **跨页面控制** |
|||
- 在任何页面都可以控制当前播放的音频 |
|||
- 提供统一的音频管理接口 |
|||
|
|||
### ✅ **资源优化** |
|||
- 避免创建多个音频实例 |
|||
- 自动清理无用的音频资源 |
|||
|
|||
### ✅ **用户体验** |
|||
- 页面跳转时音频播放不中断 |
|||
- 图标状态显示正确 |
|||
- 音频控制逻辑一致 |
|||
|
|||
## 注意事项 |
|||
|
|||
1. **页面生命周期**:音频实例与页面生命周期解耦,需要手动管理 |
|||
2. **内存管理**:确保在应用退出时正确清理音频资源 |
|||
3. **状态同步**:多个AudioControl组件需要监听相同的全局状态 |
|||
4. **错误处理**:增强错误处理机制,确保音频异常时的状态恢复 |
|||
|
|||
## 兼容性 |
|||
|
|||
- ✅ uni-app |
|||
- ✅ 小程序环境 |
|||
- ✅ H5环境 |
|||
- ✅ APP环境 |
@ -0,0 +1,144 @@ |
|||
# 音频与背景音乐交互功能说明 |
|||
|
|||
## Bug修复记录 |
|||
|
|||
### 问题描述 |
|||
当音频播放时点击背景音乐按钮,背景音乐暂停音频并开始播放。此时再点击关闭背景音乐,图标显示关闭状态但背景音乐仍在播放。 |
|||
|
|||
### 问题原因 |
|||
1. 点击背景音乐按钮时会发送事件暂停音频 |
|||
2. 音频暂停时会调用restoreBackgroundMusic恢复背景音乐 |
|||
3. 然后MusicControl再执行自己的切换逻辑 |
|||
4. 导致背景音乐被恢复后又被操作,状态混乱 |
|||
|
|||
### 解决方案 |
|||
1. **MusicControl组件优化**: |
|||
- 检测是否有音频在播放 |
|||
- 如有音频,先暂停音频,延迟执行背景音乐切换 |
|||
- 如无音频,直接切换背景音乐状态 |
|||
|
|||
2. **AudioControl组件优化**: |
|||
- 在handleBackgroundMusicToggle中只暂停音频 |
|||
- 不自动恢复背景音乐,让MusicControl自己控制 |
|||
|
|||
### 修复后的交互流程 |
|||
```mermaid |
|||
graph TD |
|||
A[点击背景音乐按钮] --> B{是否有音频播放?} |
|||
B -->|是| C[发送暂停音频事件] |
|||
C --> D[AudioControl暂停音频\n不恢复背景音乐] |
|||
D --> E[延迟100ms后切换背景音乐] |
|||
B -->|否| F[直接切换背景音乐状态] |
|||
``` |
|||
|
|||
## 实现的功能 |
|||
|
|||
### 🎵 **背景音乐控制音频** |
|||
当点击背景音乐控制按钮时: |
|||
- 如果有音频正在播放,会自动暂停音频 |
|||
- 然后正常切换背景音乐的播放/暂停状态 |
|||
|
|||
### 🎧 **音频控制背景音乐** |
|||
当点击音频控制按钮时: |
|||
- 播放音频时自动暂停背景音乐 |
|||
- 暂停音频时自动恢复背景音乐 |
|||
- 音频播放结束时自动恢复背景音乐 |
|||
|
|||
## 技术实现 |
|||
|
|||
### 事件通信机制 |
|||
使用uni-app的全局事件机制实现组件间通信: |
|||
|
|||
```javascript |
|||
// AudioControl组件发送事件 |
|||
uni.$emit('audioPlaying', true/false); |
|||
|
|||
// MusicControl组件发送事件 |
|||
uni.$emit('backgroundMusicToggle'); |
|||
|
|||
// 组件监听事件 |
|||
uni.$on('eventName', this.handlerFunction); |
|||
``` |
|||
|
|||
### 交互流程 |
|||
|
|||
#### 点击背景音乐按钮: |
|||
```mermaid |
|||
graph TD |
|||
A[点击背景音乐按钮] --> B[发送backgroundMusicToggle事件] |
|||
B --> C[AudioControl收到事件] |
|||
C --> D{音频是否在播放?} |
|||
D -->|是| E[暂停音频] |
|||
D -->|否| F[继续背景音乐操作] |
|||
E --> G[恢复背景音乐] |
|||
F --> H[切换背景音乐状态] |
|||
``` |
|||
|
|||
#### 点击音频按钮: |
|||
```mermaid |
|||
graph TD |
|||
A[点击音频按钮] --> B{当前音频状态?} |
|||
B -->|未播放| C[暂停背景音乐] |
|||
C --> D[播放音频] |
|||
D --> E[发送audioPlaying:true事件] |
|||
B -->|正在播放| F[暂停音频] |
|||
F --> G[恢复背景音乐] |
|||
G --> H[发送audioPlaying:false事件] |
|||
``` |
|||
|
|||
## 使用方法 |
|||
|
|||
在页面中同时使用两个组件: |
|||
|
|||
```vue |
|||
<template> |
|||
<view class="page-container"> |
|||
<!-- 页面内容 --> |
|||
|
|||
<!-- 音频控制组件 --> |
|||
<AudioControl :audioSrc="audioUrl" /> |
|||
|
|||
<!-- 背景音乐控制组件 --> |
|||
<MusicControl /> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import AudioControl from '@/components/AudioControl.vue'; |
|||
import MusicControl from '@/components/MusicControl.vue'; |
|||
|
|||
export default { |
|||
components: { |
|||
AudioControl, |
|||
MusicControl |
|||
}, |
|||
data() { |
|||
return { |
|||
audioUrl: this.showImg('/uploads/audio/your-audio.mp3') |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
``` |
|||
|
|||
## 优势特点 |
|||
|
|||
### ✅ **智能交互** |
|||
- 两个组件能够智能感知对方的状态 |
|||
- 避免同时播放音频和背景音乐造成的冲突 |
|||
- 提供良好的用户体验 |
|||
|
|||
### ✅ **解耦设计** |
|||
- 组件间通过事件通信,保持松耦合 |
|||
- 每个组件都能独立工作 |
|||
- 易于维护和扩展 |
|||
|
|||
### ✅ **状态同步** |
|||
- 实时同步音频和背景音乐的播放状态 |
|||
- 确保状态的一致性和准确性 |
|||
|
|||
## 注意事项 |
|||
|
|||
1. **事件监听清理**:组件销毁时会自动清理事件监听,避免内存泄漏 |
|||
2. **状态管理**:两个组件都维护各自的状态,通过事件保持同步 |
|||
3. **错误处理**:包含完善的错误处理机制,确保功能稳定运行 |
File diff suppressed because it is too large
@ -0,0 +1,92 @@ |
|||
// 全局音频管理工具
|
|||
export const GlobalAudioManager = { |
|||
// 获取当前全局音频实例
|
|||
getCurrentAudio() { |
|||
const app = getApp(); |
|||
return app && app.globalData ? app.globalData.currentAudio : null; |
|||
}, |
|||
|
|||
// 设置全局音频实例
|
|||
setCurrentAudio(audioContext) { |
|||
const app = getApp(); |
|||
if (app && app.globalData) { |
|||
app.globalData.currentAudio = audioContext; |
|||
} |
|||
}, |
|||
|
|||
// 清除全局音频实例
|
|||
clearCurrentAudio() { |
|||
const app = getApp(); |
|||
if (app && app.globalData) { |
|||
if (app.globalData.currentAudio) { |
|||
try { |
|||
app.globalData.currentAudio.stop(); |
|||
app.globalData.currentAudio.destroy(); |
|||
} catch (error) { |
|||
console.error('销毁全局音频失败:', error); |
|||
} |
|||
} |
|||
app.globalData.currentAudio = null; |
|||
} |
|||
}, |
|||
|
|||
// 检查是否有音频在播放
|
|||
isAudioPlaying() { |
|||
const audio = this.getCurrentAudio(); |
|||
return audio && !audio.paused; |
|||
}, |
|||
|
|||
// 暂停当前音频
|
|||
pauseCurrentAudio() { |
|||
const audio = this.getCurrentAudio(); |
|||
if (audio && !audio.paused) { |
|||
audio.pause(); |
|||
// 通知状态变化
|
|||
this.notifyAudioStateChange(false); |
|||
return true; |
|||
} |
|||
return false; |
|||
}, |
|||
|
|||
// 播放当前音频
|
|||
playCurrentAudio() { |
|||
const audio = this.getCurrentAudio(); |
|||
if (audio && audio.paused) { |
|||
audio.play(); |
|||
// 通知状态变化
|
|||
this.notifyAudioStateChange(true); |
|||
return true; |
|||
} |
|||
return false; |
|||
}, |
|||
|
|||
// 停止当前音频
|
|||
stopCurrentAudio() { |
|||
const audio = this.getCurrentAudio(); |
|||
if (audio) { |
|||
audio.stop(); |
|||
// 通知状态变化
|
|||
this.notifyAudioStateChange(false); |
|||
return true; |
|||
} |
|||
return false; |
|||
}, |
|||
|
|||
// 通知音频状态变化
|
|||
notifyAudioStateChange(isPlaying) { |
|||
if (typeof uni !== 'undefined') { |
|||
uni.$emit('audioPlaying', isPlaying); |
|||
} |
|||
}, |
|||
|
|||
// 获取当前音频的src
|
|||
getCurrentAudioSrc() { |
|||
const audio = this.getCurrentAudio(); |
|||
return audio ? audio.src : null; |
|||
} |
|||
}; |
|||
|
|||
// 将工具挂载到全局
|
|||
if (typeof uni !== 'undefined') { |
|||
uni.$globalAudio = GlobalAudioManager; |
|||
} |
@ -1,143 +0,0 @@ |
|||
<template> |
|||
<view style="width: 100vw;"> |
|||
<swiper class="swiper" :current="currentIndex" :vertical="true" @change="handleSwiperChange"> |
|||
<swiper-item v-for="(image, index) in swiperImages" :key="index"> |
|||
<view class="swiper-item" :style="{ backgroundImage: `url(${image})` }"> |
|||
<template v-if="index === 0"> |
|||
<view class="box"> |
|||
<!-- <view class="title">我的星槎</view> --> |
|||
<view class="subtitle subtitle1">{{info.text1}}</view> |
|||
<!-- <view class="title">我的目的地</view> --> |
|||
<view class="subtitle subtitle2">{{info.text2}}</view> |
|||
<!-- <view class="title">我的航行BGM</view> --> |
|||
<view class="subtitle subtitle3">{{info.imgTitle}}</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<template v-if="index === 1"> |
|||
<image src="https://static.ticket.sz-trip.com/epicSoul/xrcc/chapter6/img5-text.png" mode="widthFix" class="img5-text.png"></image> |
|||
</template> |
|||
|
|||
<!-- 二维码 --> |
|||
<template v-if="index === 2"> |
|||
<image src="https://static.ticket.sz-trip.com/epicSoul/bmzm/qrcode.png" mode="widthFix" class="qrcode" :show-menu-by-longpress="true"></image> |
|||
</template> |
|||
</view> |
|||
</swiper-item> |
|||
</swiper> |
|||
|
|||
<NavMenu :nav-index="navIndex" @jump-to-page="handleJumpToPage" /> |
|||
|
|||
<MusicControl /> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import MusicControl from '@/components/MusicControl.vue'; |
|||
import NavMenu from '../components/NavMenu.vue'; |
|||
export default { |
|||
components: { |
|||
MusicControl, |
|||
NavMenu |
|||
}, |
|||
data() { |
|||
return { |
|||
currentIndex: 0, |
|||
navIndex: 5, |
|||
swiperImages: [ |
|||
'', |
|||
'https://static.ticket.sz-trip.com/epicSoul/xrcc/chapter6/img5.png', |
|||
'https://static.ticket.sz-trip.com/epicSoul/bmzm/chapter5/img7.png' |
|||
], |
|||
info: {} |
|||
} |
|||
}, |
|||
onLoad(option) { |
|||
if(option) { |
|||
let data = JSON.parse(option.data) |
|||
this.info = data |
|||
console.log(data) |
|||
this.swiperImages[0] = data.imgSrc |
|||
} |
|||
}, |
|||
methods: { |
|||
handleJumpToPage(idx) { |
|||
this.navIndex = idx |
|||
}, |
|||
handleSwiperChange(e) { |
|||
this.currentIndex = e.detail.current; |
|||
}, |
|||
}, |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.swiper { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
} |
|||
|
|||
.swiper-item { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
background-size: 100% 100%; |
|||
background-color: #000; |
|||
background-repeat: no-repeat; |
|||
position: relative; |
|||
|
|||
.box { |
|||
position: absolute; |
|||
left: 0; |
|||
right: 0; |
|||
margin: 0 auto; |
|||
top: 300rpx; |
|||
text-align: center; |
|||
width: 100%; |
|||
color: rgba(220, 221, 221, 1); |
|||
|
|||
.title { |
|||
font-size: 25rpx; |
|||
margin-top: 50rpx; |
|||
} |
|||
.subtitle { |
|||
position: absolute; |
|||
font-size: 40rpx; |
|||
left: 0; |
|||
right: 0; |
|||
margin: 0 auto; |
|||
} |
|||
.subtitle1 { |
|||
top: 150rpx; |
|||
} |
|||
.subtitle2 { |
|||
top: 280rpx; |
|||
} |
|||
.subtitle3 { |
|||
top: 410rpx; |
|||
} |
|||
} |
|||
|
|||
.img5-text.png { |
|||
width: 312.58rpx; |
|||
position: absolute; |
|||
left: 0; |
|||
right: 0; |
|||
margin: 0 auto; |
|||
bottom: 492rpx; |
|||
} |
|||
} |
|||
|
|||
.swiper-img { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
} |
|||
|
|||
.qrcode { |
|||
position: absolute; |
|||
left: 0; |
|||
right: 0; |
|||
margin: 0 auto; |
|||
width: 25vw; |
|||
bottom: 28vh; |
|||
} |
|||
</style> |
@ -1,75 +0,0 @@ |
|||
<template> |
|||
<view style="width: 100vw;"> |
|||
<swiper class="swiper" :current="currentIndex" :vertical="true" @change="handleSwiperChange"> |
|||
<swiper-item v-for="(image, index) in swiperImages" :key="index"> |
|||
<view class="swiper-item" :style="{ backgroundImage: `url(${image})` }"> |
|||
|
|||
</view> |
|||
</swiper-item> |
|||
</swiper> |
|||
|
|||
<NavMenu :nav-index="navIndex" @jump-to-page="handleJumpToPage" /> |
|||
|
|||
<MusicControl /> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import MusicControl from '@/components/MusicControl.vue'; |
|||
import NavMenu from '../components/NavMenu.vue'; |
|||
export default { |
|||
components: { |
|||
MusicControl, |
|||
NavMenu |
|||
}, |
|||
data() { |
|||
return { |
|||
currentIndex: 0, |
|||
navIndex: 3, |
|||
swiperImages: [ |
|||
'https://static.ticket.sz-trip.com/epicSoul/xrcc/home/img1.gif', |
|||
] |
|||
} |
|||
}, |
|||
onLoad(option) { |
|||
this.currentIndex = option.currentIndex || 0 |
|||
if (this.currentIndex == this.swiperImages.length - 1) this.navIndex = 1; |
|||
}, |
|||
methods: { |
|||
handleJumpToPage(idx) { |
|||
this.navIndex = idx |
|||
}, |
|||
handleSwiperChange(e) { |
|||
this.currentIndex = e.detail.current; |
|||
}, |
|||
}, |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.swiper { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
} |
|||
|
|||
.swiper-item { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
background-size: 100% auto; |
|||
background-color: #000; |
|||
background-repeat: no-repeat; |
|||
position: relative; |
|||
|
|||
.btn-img { |
|||
position: absolute; |
|||
width: 149.8rpx; |
|||
bottom: 290rpx; |
|||
left: 84rpx; |
|||
} |
|||
} |
|||
|
|||
.swiper-img { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
} |
|||
</style> |
Loading…
Reference in new issue