1、调整王府井背景图
2、调整福印收集逻辑,必须是发生互动后才能进行收集
3、王府井增加热区点击,点击后展示图片浏览功能
4、图片浏览组件增加两种浏览模式,有标题和没有标题
This commit is contained in:
Wenzhe 2026-02-03 22:55:40 +08:00
parent ecb0bcda46
commit ab75f0056c
12 changed files with 164 additions and 127 deletions

View File

@ -1,6 +1,5 @@
<script setup> <script setup>
import { ref, onMounted, computed } from 'vue' import { ref, onMounted, computed } from 'vue'
import FuClickArea from './FuClickArea.vue'
// //
const props = defineProps({ const props = defineProps({
@ -23,7 +22,6 @@ const emit = defineEmits(['collect-seal'])
const sealCollected = ref(false) const sealCollected = ref(false)
// //
const fuClickAreaVisible = ref(true)
const sq2ImageVisible = ref(false) const sq2ImageVisible = ref(false)
// //
@ -32,15 +30,15 @@ const parallaxOffset = computed(() => {
return props.scrollPosition * 0.1 return props.scrollPosition * 0.1
}) })
//
const handleFuClick = () => {
fuClickAreaVisible.value = false
sq2ImageVisible.value = true
emit('collect-seal')
}
// webview // webview
const openWebview = () => { const openWebview = () => {
//
if (!sealCollected.value) {
sealCollected.value = true
sq2ImageVisible.value = true
emit('collect-seal')
}
uni.navigateTo({ uni.navigateTo({
url: '/pages/webview/webview', url: '/pages/webview/webview',
success: () => { success: () => {
@ -75,16 +73,7 @@ onMounted(() => {
<img src="/static/bg/bg2.jpg" alt="崇文门商圈" class="background-image" /> <img src="/static/bg/bg2.jpg" alt="崇文门商圈" class="background-image" />
</div> </div>
<!-- 福字点击区域 -->
<FuClickArea
:visible="fuClickAreaVisible"
:x-range="630"
:y-range="300"
:y-start="150"
:fu-width="100"
:fu-height="100"
@click="handleFuClick"
/>
<!-- sq2图片 --> <!-- sq2图片 -->
<img <img

View File

@ -5,14 +5,14 @@
<div class="modal-body"> <div class="modal-body">
<div class="couplet-display"> <div class="couplet-display">
<!-- 显示海报图片后端直接返回 --> <!-- 显示海报图片后端直接返回 -->
<div class="poster-image" v-if="couplet.image_url"> <div class="poster-image" v-if="couplet?.image_url">
<!-- 加载提示 --> <!-- 加载提示 -->
<div class="image-loading" v-if="isLoading"> <div class="image-loading" v-if="isLoading">
<div class="loading-spinner"></div> <div class="loading-spinner"></div>
<div class="loading-text">海报加载中...</div> <div class="loading-text">海报加载中...</div>
</div> </div>
<img <img
:src="couplet.image_url" :src="couplet?.image_url"
alt="春联海报" alt="春联海报"
style="width: 100%; max-width: 300px; height: auto;" style="width: 100%; max-width: 300px; height: auto;"
@load="isLoading = false" @load="isLoading = false"
@ -58,7 +58,7 @@ const emit = defineEmits(['close'])
const isLoading = ref(true) const isLoading = ref(true)
// couplet // couplet
watch(() => props.couplet.image_url, (newImageUrl) => { watch(() => props.couplet?.image_url, (newImageUrl) => {
if (newImageUrl) { if (newImageUrl) {
isLoading.value = true isLoading.value = true
} }
@ -66,7 +66,7 @@ watch(() => props.couplet.image_url, (newImageUrl) => {
// visible // visible
watch(() => props.visible, (newVisible) => { watch(() => props.visible, (newVisible) => {
if (newVisible && props.couplet.image_url) { if (newVisible && props.couplet?.image_url) {
isLoading.value = true isLoading.value = true
} }
}) })

View File

@ -1,6 +1,5 @@
<script setup> <script setup>
import { ref, onMounted, onUnmounted, computed, getCurrentInstance as vueGetCurrentInstance, watch } from 'vue' import { ref, onMounted, onUnmounted, computed, getCurrentInstance as vueGetCurrentInstance, watch } from 'vue'
import FuClickArea from './FuClickArea.vue'
import ImagePreloader from '@/utils/preload' import ImagePreloader from '@/utils/preload'
// Vue // Vue
@ -20,22 +19,16 @@ const props = defineProps({
const emit = defineEmits(['collect-seal']) const emit = defineEmits(['collect-seal'])
// // sq3
const fuClickAreaVisible = ref(true)
const sq3ImageVisible = ref(false) const sq3ImageVisible = ref(false)
//
const sealCollected = ref(false)
// //
const parallaxOffset = computed(() => { const parallaxOffset = computed(() => {
return props.scrollPosition * 0.1 return props.scrollPosition * 0.1
}) })
//
const handleFuClick = () => {
fuClickAreaVisible.value = false
sq3ImageVisible.value = true
emit('collect-seal')
}
// //
const getDzmImageUrl = (name) => { const getDzmImageUrl = (name) => {
return new URL(`/static/dzm/${name}`, import.meta.url).href return new URL(`/static/dzm/${name}`, import.meta.url).href
@ -449,6 +442,13 @@ const handleTouchEnd = () => {
// Canvas // Canvas
canvasDisabled.value = true canvasDisabled.value = true
console.log('Canvas 触摸事件已禁用') console.log('Canvas 触摸事件已禁用')
// sq3
if (!sealCollected.value) {
sq3ImageVisible.value = true
sealCollected.value = true
emit('collect-seal')
}
} else { } else {
// //
console.log('未放入目标区域,回归原始位置') console.log('未放入目标区域,回归原始位置')
@ -553,17 +553,6 @@ onUnmounted(() => {
<image src="/static/bg/bg5.jpg" alt="东直门商圈" class="background-image" mode="widthFix" /> <image src="/static/bg/bg5.jpg" alt="东直门商圈" class="background-image" mode="widthFix" />
</view> </view>
<!-- 福字点击区域 -->
<FuClickArea
:visible="fuClickAreaVisible"
:x-range="630"
:y-range="400"
:y-start="150"
:fu-width="100"
:fu-height="100"
@click="handleFuClick"
/>
<!-- sq3图片 --> <!-- sq3图片 -->
<image <image
v-if="sq3ImageVisible" v-if="sq3ImageVisible"

View File

@ -17,6 +17,11 @@ const props = defineProps({
currentIndex: { currentIndex: {
type: Number, type: Number,
default: 0 default: 0
},
// 'with-title' 'without-title'
mode: {
type: String,
default: 'with-title'
} }
}) })
@ -63,14 +68,22 @@ const handleOverlayClick = () => {
<div class="modal-overlay" @click="handleOverlayClick"></div> <div class="modal-overlay" @click="handleOverlayClick"></div>
<!-- 弹窗内容 --> <!-- 弹窗内容 -->
<div class="modal-content"> <div class="modal-content" :class="{ 'without-title': mode === 'without-title' }">
<!-- 图片区域 --> <!-- 图片区域 -->
<div class="gallery-image-wrapper"> <div class="gallery-image-wrapper">
<!-- 无标题模式下的导航按钮 -->
<div v-if="mode === 'without-title'" class="nav-btn prev-btn side-btn" @click="prevImage">
<img src="/static/images/btn_prev.png" alt="上一张" class="nav-icon" />
</div>
<img :src="images[currentImageIndex]?.src" :alt="images[currentImageIndex]?.title" class="gallery-image" /> <img :src="images[currentImageIndex]?.src" :alt="images[currentImageIndex]?.title" class="gallery-image" />
<!-- 无标题模式下的导航按钮 -->
<div v-if="mode === 'without-title'" class="nav-btn next-btn side-btn" @click="nextImage">
<img src="/static/images/btn_next.png" alt="下一张" class="nav-icon" />
</div>
</div> </div>
<!-- 控制区域 --> <!-- 有标题模式下的控制区域 -->
<div class="gallery-controls"> <div v-if="mode === 'with-title'" class="gallery-controls">
<div class="nav-btn prev-btn" @click="prevImage"> <div class="nav-btn prev-btn" @click="prevImage">
<img src="/static/images/btn_prev.png" alt="上一张" class="nav-icon" /> <img src="/static/images/btn_prev.png" alt="上一张" class="nav-icon" />
</div> </div>
@ -125,6 +138,36 @@ const handleOverlayClick = () => {
animation: modalIn 0.3s ease; animation: modalIn 0.3s ease;
} }
/* 无标题模式下的弹窗内容 */
.modal-content.without-title {
padding-bottom: 30rpx;
}
/* 图片区域 */
.gallery-image-wrapper {
position: relative;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
/* 无标题模式下的侧边导航按钮 */
.side-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 10;
}
.side-btn.prev-btn {
left: -40rpx;
}
.side-btn.next-btn {
right: -40rpx;
}
@keyframes modalIn { @keyframes modalIn {
from { from {
opacity: 0; opacity: 0;

View File

@ -1,6 +1,5 @@
<script setup> <script setup>
import { ref, onMounted, computed, watch } from 'vue' import { ref, onMounted, computed, watch } from 'vue'
import FuClickArea from './FuClickArea.vue'
import ImageGalleryModal from './ImageGalleryModal.vue' import ImageGalleryModal from './ImageGalleryModal.vue'
import ImagePreloader from '@/utils/preload'; import ImagePreloader from '@/utils/preload';
@ -21,9 +20,10 @@ const props = defineProps({
// //
const emit = defineEmits(['collect-seal']) const emit = defineEmits(['collect-seal'])
// // sq3
const fuClickAreaVisible = ref(true)
const sq3ImageVisible = ref(false) const sq3ImageVisible = ref(false)
//
const sealCollected = ref(false)
// //
const imagesLoaded = ref(false) const imagesLoaded = ref(false)
@ -60,12 +60,7 @@ watch(() => props.active, async (newActive) => {
} }
}) })
//
const handleFuClick = () => {
fuClickAreaVisible.value = false
sq3ImageVisible.value = true
emit('collect-seal')
}
// //
const getImageUrl = (name) => { const getImageUrl = (name) => {
@ -87,6 +82,13 @@ const currentGalleryIndex = ref(0)
const openGallery = (index) => { const openGallery = (index) => {
currentGalleryIndex.value = index currentGalleryIndex.value = index
galleryVisible.value = true galleryVisible.value = true
// sq3
if (!sealCollected.value) {
sq3ImageVisible.value = true
sealCollected.value = true
emit('collect-seal')
}
} }
// //
@ -117,17 +119,6 @@ onMounted(() => {
<img src="/static/bg/bg4.jpg" alt="隆福寺商圈" class="background-image" /> <img src="/static/bg/bg4.jpg" alt="隆福寺商圈" class="background-image" />
</div> </div>
<!-- 福字点击区域 -->
<FuClickArea
:visible="fuClickAreaVisible"
:x-range="630"
:y-range="400"
:y-start="150"
:fu-width="100"
:fu-height="100"
@click="handleFuClick"
/>
<!-- sq3图片 --> <!-- sq3图片 -->
<img <img
v-if="sq3ImageVisible" v-if="sq3ImageVisible"
@ -161,7 +152,8 @@ onMounted(() => {
<ImageGalleryModal <ImageGalleryModal
:visible="galleryVisible" :visible="galleryVisible"
:images="lfsImages" :images="lfsImages"
:currentIndex="currentGalleryIndex" :current-index="currentGalleryIndex"
mode="with-title"
@close="closeGallery" @close="closeGallery"
/> />

View File

@ -1,6 +1,5 @@
<script setup> <script setup>
import { ref, onMounted, onUnmounted, computed, watch } from 'vue' import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
import FuClickArea from './FuClickArea.vue'
// //
const props = defineProps({ const props = defineProps({
@ -27,7 +26,6 @@ const isMusicPlaying = ref(false)
const musicPlayer = ref(null) const musicPlayer = ref(null)
// //
const fuClickAreaVisible = ref(true)
const sq1ImageVisible = ref(false) const sq1ImageVisible = ref(false)
// //
@ -72,15 +70,15 @@ const parallaxOffset = computed(() => {
return offset return offset
}) })
//
const handleFuClick = () => {
fuClickAreaVisible.value = false
sq1ImageVisible.value = true
emit('collect-seal')
}
// / // /
const toggleMusic = () => { const toggleMusic = () => {
//
if (!sealCollected.value) {
sealCollected.value = true
sq1ImageVisible.value = true
emit('collect-seal')
}
if (!musicPlayer.value) { if (!musicPlayer.value) {
// //
musicPlayer.value = uni.createInnerAudioContext() musicPlayer.value = uni.createInnerAudioContext()
@ -182,16 +180,7 @@ onUnmounted(() => {
/> />
</div> </div>
<!-- 福字点击区域 -->
<FuClickArea
:visible="fuClickAreaVisible"
:x-range="630"
:y-range="400"
:y-start="350"
:fu-width="100"
:fu-height="100"
@click="handleFuClick"
/>
<!-- sq1图片 --> <!-- sq1图片 -->
<img <img

View File

@ -1,6 +1,6 @@
<script setup> <script setup>
import { ref, onMounted, computed, watch } from 'vue' import { ref, onMounted, computed, watch } from 'vue'
import FuClickArea from './FuClickArea.vue' import ImageGalleryModal from './ImageGalleryModal.vue'
import ImagePreloader from '@/utils/preload'; import ImagePreloader from '@/utils/preload';
// //
@ -21,7 +21,6 @@ const props = defineProps({
const emit = defineEmits(['collect-seal']) const emit = defineEmits(['collect-seal'])
// //
const fuClickAreaVisible = ref(true)
const sq3ImageVisible = ref(false) const sq3ImageVisible = ref(false)
// //
@ -59,13 +58,6 @@ watch(() => props.active, async (newActive) => {
} }
}) })
//
const handleFuClick = () => {
fuClickAreaVisible.value = false
sq3ImageVisible.value = true
emit('collect-seal')
}
// URL // URL
const generateImageUrl = (name) => { const generateImageUrl = (name) => {
const url = new URL(`/static/wfj/${name}.jpg`, import.meta.url) const url = new URL(`/static/wfj/${name}.jpg`, import.meta.url)
@ -80,15 +72,26 @@ const images = [
{ src: generateImageUrl('img3'), title: '' }, { src: generateImageUrl('img3'), title: '' },
{ src: generateImageUrl('img4'), title: '' } { src: generateImageUrl('img4'), title: '' }
] ]
const currentImageIndex = ref(0)
// //
const prevImage = () => { const galleryVisible = ref(false)
currentImageIndex.value = (currentImageIndex.value - 1 + images.length) % images.length const currentGalleryIndex = ref(0)
//
const openGallery = (index) => {
currentGalleryIndex.value = index
galleryVisible.value = true
// sq3
if (!sq3ImageVisible.value) {
sq3ImageVisible.value = true
emit('collect-seal')
}
} }
const nextImage = () => { //
currentImageIndex.value = (currentImageIndex.value + 1) % images.length const closeGallery = () => {
galleryVisible.value = false
} }
// //
@ -114,17 +117,6 @@ onMounted(() => {
<img src="/static/bg/bg3.jpg" alt="王府井商圈" class="background-image" /> <img src="/static/bg/bg3.jpg" alt="王府井商圈" class="background-image" />
</div> </div>
<!-- 福字点击区域 -->
<FuClickArea
:visible="fuClickAreaVisible"
:x-range="630"
:y-range="900"
:y-start="150"
:fu-width="100"
:fu-height="100"
@click="handleFuClick"
/>
<!-- sq3图片 --> <!-- sq3图片 -->
<img <img
v-if="sq3ImageVisible" v-if="sq3ImageVisible"
@ -133,22 +125,22 @@ onMounted(() => {
class="sq3-image" class="sq3-image"
/> />
<!-- 图片浏览组件 --> <!-- 热点点击区域1 -->
<div class="image-gallery"> <div class="hotspot-area" style="left: 163rpx; top: 950rpx;" @click="openGallery(0)">
<div class="gallery-image-wrapper"> <div class="pulse-indicator">
<div class="loading-overlay" v-if="isPreloading"> <div class="pulse-circle"></div>
<div class="loading-spinner"></div>
</div>
<div class="nav-btn prev-btn" @click="prevImage">
<img src="/static/images/btn_prev.png" alt="上一张" class="nav-icon" />
</div>
<img :src="images[currentImageIndex].src" :alt="images[currentImageIndex].title" mode="widthFit" class="gallery-image" />
<div class="nav-btn next-btn" @click="nextImage">
<img src="/static/images/btn_next.png" alt="下一张" class="nav-icon" />
</div>
</div> </div>
</div> </div>
<!-- 图片浏览器弹窗 -->
<ImageGalleryModal
:visible="galleryVisible"
:images="images"
:current-index="currentGalleryIndex"
mode="without-title"
@close="closeGallery"
/>
</section> </section>
</template> </template>
@ -502,4 +494,47 @@ onMounted(() => {
0% { transform: rotate(0deg); } 0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); } 100% { transform: rotate(360deg); }
} }
/* 热点点击区域 */
.hotspot-area {
position: absolute;
width: 150rpx;
height: 150rpx;
cursor: pointer;
z-index: 25;
display: flex;
align-items: center;
justify-content: center;
}
/* 脉冲动效 */
.pulse-indicator {
position: relative;
width: 100rpx;
height: 100rpx;
}
.pulse-circle {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 100%;
border-radius: 50%;
background-color: rgba(255, 215, 0, 0.4);
border: 3rpx solid rgba(255, 215, 0, 0.8);
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% {
transform: translate(-50%, -50%) scale(0.8);
opacity: 0.8;
}
100% {
transform: translate(-50%, -50%) scale(2);
opacity: 0;
}
}
</style> </style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 457 KiB

After

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB