1、更换loading页面开始按钮
 2、长图页五个商圈分别增加对应的视频地址
 3、视频播放按钮吉视频播放器,分别封装为组件,5个商圈组件进行引用
This commit is contained in:
Wenzhe 2026-02-04 12:18:51 +08:00
parent d89ce10c34
commit cea4f8b67d
10 changed files with 368 additions and 73 deletions

View File

@ -1,5 +1,7 @@
<script setup>
import { ref, onMounted, computed } from 'vue'
import VideoPlayButton from './VideoPlayButton.vue'
import VideoPlayerModal from './VideoPlayerModal.vue'
//
const props = defineProps({
@ -12,6 +14,11 @@ const props = defineProps({
scrollPosition: {
type: Number,
default: 0
},
//
videoUrl: {
type: String,
default: ''
}
})
@ -23,6 +30,8 @@ const sealCollected = ref(false)
//
const sq2ImageVisible = ref(false)
//
const showVideoPlayer = ref(false)
//
const parallaxOffset = computed(() => {
@ -46,15 +55,25 @@ const openWebview = () => {
},
fail: (err) => {
console.error('Failed to open webview:', err)
showToast({
message: '页面打开失败',
icon: 'error',
uni.showToast({
title: '页面打开失败',
icon: 'none',
duration: 1500
})
}
})
}
//
const openVideoPlayer = () => {
showVideoPlayer.value = true
}
//
const closeVideoPlayer = () => {
showVideoPlayer.value = false
}
//
onMounted(() => {
//
@ -91,6 +110,17 @@ onMounted(() => {
@click="openWebview"
/>
<!-- 视频播放按钮 -->
<VideoPlayButton @play="openVideoPlayer" />
<!-- 视频播放器弹窗 -->
<VideoPlayerModal
:video-url="props.videoUrl"
:visible="showVideoPlayer"
@close="closeVideoPlayer"
/>
</section>
</template>

View File

@ -1,6 +1,8 @@
<script setup>
import { ref, onMounted, onUnmounted, computed, getCurrentInstance as vueGetCurrentInstance, watch } from 'vue'
import ImagePreloader from '@/utils/preload'
import VideoPlayButton from './VideoPlayButton.vue'
import VideoPlayerModal from './VideoPlayerModal.vue'
// Vue
const instance = vueGetCurrentInstance()
@ -14,6 +16,10 @@ const props = defineProps({
scrollPosition: {
type: Number,
default: 0
},
videoUrl: {
type: String,
default: ''
}
})
@ -23,6 +29,8 @@ const emit = defineEmits(['collect-seal'])
const sq3ImageVisible = ref(false)
//
const sealCollected = ref(false)
//
const showVideoPlayer = ref(false)
//
const parallaxOffset = computed(() => {
@ -467,6 +475,16 @@ const resetDuckPosition = () => {
console.log('鸭子已回归原始位置:', duckX.value, duckY.value)
}
//
const openVideoPlayer = () => {
showVideoPlayer.value = true
}
//
const closeVideoPlayer = () => {
showVideoPlayer.value = false
}
//
const preloadImages = async () => {
if (imagesLoaded.value || isPreloading.value) return
@ -590,6 +608,17 @@ onUnmounted(() => {
class="drag-trigger-area"
@touchstart="startDrag"
></view>
<!-- 视频播放按钮 -->
<VideoPlayButton @play="openVideoPlayer" />
<!-- 视频播放器弹窗 -->
<VideoPlayerModal
:video-url="props.videoUrl"
:visible="showVideoPlayer"
@close="closeVideoPlayer"
/>
</view>
</template>
@ -692,4 +721,6 @@ onUnmounted(() => {
height: 100rpx;
z-index: 30;
}
</style>

View File

@ -111,7 +111,7 @@ onMounted(() => {
</div>
<!-- 进度条区域 -->
<div class="progress-container">
<div v-if="!startButtonVisible" class="progress-container">
<div class="progress-bar">
<div class="progress-fill" :style="{ width: `${loadingProgress}%` }"></div>
</div>
@ -190,8 +190,8 @@ onMounted(() => {
.start-button {
position: absolute;
bottom: 20vh;
width: 300rpx;
height: 100rpx;
width: 496rpx;
height: 97rpx;
cursor: pointer;
animation: pulse 1.5s infinite;
}

View File

@ -1,6 +1,8 @@
<script setup>
import { ref, onMounted, computed, watch } from 'vue'
import ImageGalleryModal from './ImageGalleryModal.vue'
import VideoPlayButton from './VideoPlayButton.vue'
import VideoPlayerModal from './VideoPlayerModal.vue'
import ImagePreloader from '@/utils/preload';
//
@ -14,6 +16,11 @@ const props = defineProps({
scrollPosition: {
type: Number,
default: 0
},
//
videoUrl: {
type: String,
default: ''
}
})
@ -24,6 +31,8 @@ const emit = defineEmits(['collect-seal'])
const sq3ImageVisible = ref(false)
//
const sealCollected = ref(false)
//
const showVideoPlayer = ref(false)
//
const imagesLoaded = ref(false)
@ -96,6 +105,16 @@ const closeGallery = () => {
galleryVisible.value = false
}
//
const openVideoPlayer = () => {
showVideoPlayer.value = true
}
//
const closeVideoPlayer = () => {
showVideoPlayer.value = false
}
//
onMounted(() => {
//
@ -157,6 +176,18 @@ onMounted(() => {
@close="closeGallery"
/>
<!-- 视频播放按钮 -->
<VideoPlayButton @play="openVideoPlayer" />
<!-- 视频播放器弹窗 -->
<VideoPlayerModal
:video-url="props.videoUrl"
:visible="showVideoPlayer"
@close="closeVideoPlayer"
/>
</section>
</template>
@ -305,53 +336,6 @@ onMounted(() => {
}
}
/* 烟花效果 */
.fireworks {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 20;
}
.firework {
position: absolute;
font-size: 2rem;
opacity: 0;
animation: firework 3s infinite;
}
.firework-1 {
top: 10%;
left: 20%;
animation-delay: 0s;
}
.firework-2 {
top: 15%;
right: 25%;
animation-delay: 1s;
}
.firework-3 {
top: 8%;
right: 15%;
animation-delay: 2s;
}
.firework-4 {
top: 12%;
left: 25%;
animation-delay: 3s;
}
@keyframes firework {
0%, 100% { opacity: 0; transform: scale(0); }
50% { opacity: 1; transform: scale(1.5); }
}
/* 福印收集标记 */
.seal-collected-mark {
position: absolute;
@ -443,19 +427,4 @@ onMounted(() => {
}
}
/* 响应式设计 */
@media (max-width: 640px) {
.fu-word {
font-size: 1.5rem;
}
.lantern {
font-size: 2rem;
}
.interaction-area {
width: 100px;
height: 80px;
}
}
</style>

View File

@ -1,5 +1,7 @@
<script setup>
import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
import VideoPlayButton from './VideoPlayButton.vue'
import VideoPlayerModal from './VideoPlayerModal.vue'
//
const props = defineProps({
@ -12,6 +14,11 @@ const props = defineProps({
scrollPosition: {
type: Number,
default: 0
},
//
videoUrl: {
type: String,
default: ''
}
})
@ -27,6 +34,8 @@ const musicPlayer = ref(null)
//
const sq1ImageVisible = ref(false)
//
const showVideoPlayer = ref(false)
//
const sceneHeight = ref(0)
@ -144,6 +153,16 @@ const calculateHeight = () => {
}
}
//
const openVideoPlayer = () => {
showVideoPlayer.value = true
}
//
const closeVideoPlayer = () => {
showVideoPlayer.value = false
}
//
onUnmounted(() => {
if (musicPlayer.value) {
@ -194,6 +213,12 @@ onUnmounted(() => {
<!-- 舞狮动画 -->
<div class="lion-dance"></div>
<!-- 视频播放按钮 -->
<VideoPlayButton @play="openVideoPlayer" />
<!-- 视频播放器弹窗 -->
<VideoPlayerModal :video-url="props.videoUrl" :visible="showVideoPlayer" @close="closeVideoPlayer" />
</section>
</template>

View File

@ -78,31 +78,36 @@ const scenes = ref([
id: 'dongzhimen',
name: '东直门商圈',
description: '京城东部的交通枢纽和商业中心',
collectedItem: '团圆福筷'
collectedItem: '团圆福筷',
videoUrl: 'http://192.168.2.149:8090/sample-3.mp4'
},
{
id: 'longfusi',
name: '隆福寺商圈',
description: '传统文化与现代艺术融合的潮流地标',
collectedItem: '文化福灯'
collectedItem: '文化福灯',
videoUrl: 'http://192.168.2.149:8090/sample-3.mp4'
},
{
id: 'wangfujing',
name: '王府井商圈',
description: '北京最繁华的商业中心之一',
collectedItem: '金袋福卡'
collectedItem: '金袋福卡',
videoUrl: 'http://192.168.2.149:8090/sample-3.mp4'
},
{
id: 'chongwen',
name: '崇文门商圈',
description: '融合传统文化与现代商业的活力区域',
collectedItem: '国潮福字'
collectedItem: '国潮福字',
videoUrl: 'http://192.168.2.149:8090/sample-3.mp4'
},
{
id: 'qianmen',
name: '前门商圈',
description: '北京最具历史文化底蕴的商圈之一',
collectedItem: '非遗福印'
collectedItem: '非遗福印',
videoUrl: 'http://192.168.2.149:8090/sample-3.mp4'
},
{
id: 'home',
@ -611,6 +616,7 @@ onUnmounted(() => {
<DongzhimenScene
:active="activeSceneIndex === 1"
:scroll-position="scrollContainer && scrollContainer.value ? scrollContainer.value.scrollTop : 0"
:video-url="scenes[1].videoUrl"
@collect-seal="collectSeal(1)"
/>
@ -618,6 +624,7 @@ onUnmounted(() => {
<LongfusiScene
:active="activeSceneIndex === 2"
:scroll-position="scrollContainer && scrollContainer.value ? scrollContainer.value.scrollTop : 0"
:video-url="scenes[2].videoUrl"
@collect-seal="collectSeal(2)"
/>
@ -625,6 +632,7 @@ onUnmounted(() => {
<WangfujingScene
:active="activeSceneIndex === 3"
:scroll-position="scrollContainer?.value?.scrollTop || 0"
:video-url="scenes[3].videoUrl"
@collect-seal="collectSeal(3)"
/>
@ -632,6 +640,7 @@ onUnmounted(() => {
<ChongwenScene
:active="activeSceneIndex === 4"
:scroll-position="scrollContainer?.value?.scrollTop || 0"
:video-url="scenes[4].videoUrl"
@collect-seal="collectSeal(4)"
/>
@ -639,6 +648,7 @@ onUnmounted(() => {
<QianmenScene
:active="activeSceneIndex === 5"
:scroll-position="scrollContainer?.value?.scrollTop || 0"
:video-url="scenes[5].videoUrl"
@collect-seal="collectSeal(5)"
@height-changed="handleQianmenHeightChanged"
/>

View File

@ -0,0 +1,94 @@
<script setup>
import { defineEmits } from 'vue'
//
const emit = defineEmits(['play'])
//
const handlePlayClick = () => {
emit('play')
}
</script>
<template>
<div class="video-play-button-container" @click="handlePlayClick">
<!-- 背景层单独进行呼吸动画 -->
<div class="video-play-button-bg"></div>
<!-- 内容层保持静态 -->
<div class="video-play-button-content">
<img src="/static/images/icon_music1.png" alt="播放视频" class="video-icon" />
<span class="video-text">观看视频</span>
</div>
</div>
</template>
<style scoped>
/* 视频播放按钮容器 */
.video-play-button-container {
position: absolute;
bottom: 100rpx;
right: 5rpx;
width: 120rpx;
height: 120rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 50;
cursor: pointer;
}
/* 背景层,单独进行呼吸动画 */
.video-play-button-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
background-color: rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
animation: breathe 2s infinite ease-in-out;
}
/* 内容层,保持静态 */
.video-play-button-content {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* 鼠标悬停效果 */
.video-play-button-container:hover .video-play-button-bg {
background-color: rgba(0, 0, 0, 0.9);
transform: scale(1.05);
animation: none;
}
.video-icon {
width: 40rpx;
height: 40rpx;
margin-bottom: 10rpx;
}
.video-text {
font-size: 24rpx;
color: white;
text-align: center;
}
/* 呼吸动画 */
@keyframes breathe {
0%, 100% {
transform: scale(1);
opacity: 0.8;
}
50% {
transform: scale(1.1);
opacity: 1;
}
}
</style>

View File

@ -0,0 +1,105 @@
<script setup>
import { defineProps, defineEmits } from 'vue'
//
const props = defineProps({
//
videoUrl: {
type: String,
default: ''
},
//
visible: {
type: Boolean,
default: false
}
})
//
const emit = defineEmits(['close'])
//
const handleClose = () => {
emit('close')
}
//
const handleModalClick = () => {
emit('close')
}
//
const handleContentClick = (e) => {
e.stopPropagation()
}
</script>
<template>
<div v-if="visible" class="video-modal">
<div class="video-modal-content" @click="handleContentClick">
<div class="video-close-button" @click="handleClose">
<span>×</span>
</div>
<video
:src="videoUrl"
class="video-player"
controls
autoplay
loop
></video>
</div>
</div>
</template>
<style scoped>
/* 视频播放器弹窗样式 */
.video-modal {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.9);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.video-modal-content {
position: relative;
border-radius: 20rpx;
overflow: hidden;
width: 720rpx;
height: 405rpx;
}
.video-close-button {
position: absolute;
top: 10rpx;
right: 10rpx;
width: 40rpx;
height: 40rpx;
background-color: rgba(0, 0, 0, 0.7);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
}
.video-close-button span {
color: white;
font-size: 30rpx;
font-weight: bold;
line-height: 30rpx;
height: 30rpx;
}
.video-player {
width: 100%;
height: 100%;
background-color: #000;
}
</style>

View File

@ -1,6 +1,8 @@
<script setup>
import { ref, onMounted, computed, watch } from 'vue'
import ImageGalleryModal from './ImageGalleryModal.vue'
import VideoPlayButton from './VideoPlayButton.vue'
import VideoPlayerModal from './VideoPlayerModal.vue'
import ImagePreloader from '@/utils/preload';
//
@ -14,6 +16,11 @@ const props = defineProps({
scrollPosition: {
type: Number,
default: 0
},
//
videoUrl: {
type: String,
default: ''
}
})
@ -22,6 +29,8 @@ const emit = defineEmits(['collect-seal'])
//
const sq3ImageVisible = ref(false)
//
const showVideoPlayer = ref(false)
//
const imagesLoaded = ref(false)
@ -94,6 +103,16 @@ const closeGallery = () => {
galleryVisible.value = false
}
//
const openVideoPlayer = () => {
showVideoPlayer.value = true
}
//
const closeVideoPlayer = () => {
showVideoPlayer.value = false
}
//
onMounted(() => {
//
@ -141,6 +160,17 @@ onMounted(() => {
@close="closeGallery"
/>
<!-- 视频播放按钮 -->
<VideoPlayButton @play="openVideoPlayer" />
<!-- 视频播放器弹窗 -->
<VideoPlayerModal
:video-url="props.videoUrl"
:visible="showVideoPlayer"
@close="closeVideoPlayer"
/>
</section>
</template>
@ -537,4 +567,5 @@ onMounted(() => {
opacity: 0;
}
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 42 KiB