1、崇文门模块初步完成
2、增加特色主题区展示页面,设置对应的导航条颜色
3、福字点击区域封装为组件
This commit is contained in:
Wenzhe 2026-01-29 16:39:58 +08:00
parent 5debfabc01
commit 3dcc2df0c1
5 changed files with 300 additions and 168 deletions

View File

@ -1,5 +1,6 @@
<script setup>
import { ref, onMounted, computed } from 'vue'
import FuClickArea from './FuClickArea.vue'
//
const props = defineProps({
@ -21,34 +22,39 @@ const emit = defineEmits(['collect-seal', 'play-drum'])
//
const sealCollected = ref(false)
//
const fuClickAreaVisible = ref(true)
const sq2ImageVisible = ref(false)
//
const parallaxOffset = computed(() => {
// 1/10
return props.scrollPosition * 0.1
})
//
const collectSeal = () => {
if (!sealCollected.value) {
sealCollected.value = true
//
const handleFuClick = () => {
fuClickAreaVisible.value = false
sq2ImageVisible.value = true
emit('collect-seal')
showToast({
message: '恭喜获得国潮福字!',
icon: 'success',
duration: 2000
})
}
}
//
const playDrum = () => {
emit('play-drum')
collectSeal()
// webview
const openWebview = () => {
uni.navigateTo({
url: '/pages/webview/webview',
success: () => {
console.log('Webview opened successfully')
},
fail: (err) => {
console.error('Failed to open webview:', err)
showToast({
message: '滑动探索商圈,收集国潮福字!',
icon: 'info',
message: '页面打开失败',
icon: 'error',
duration: 1500
})
}
})
}
//
@ -69,41 +75,33 @@ onMounted(() => {
<img src="/static/bg/bg2.jpg" alt="崇文门商圈" class="background-image" />
</div>
<!-- 增强动效层 -->
<div class="enhancement-layer">
<!-- 灯笼增强动效 -->
<div class="lanterns">
<div class="lantern left-lantern">🏮</div>
<div class="lantern right-lantern">🏮</div>
</div>
<!-- 福字点击区域 -->
<FuClickArea
:visible="fuClickAreaVisible"
:x-range="630"
:y-range="400"
:y-start="150"
:fu-width="100"
:fu-height="100"
@click="handleFuClick"
/>
<!-- 福字增强动效 -->
<div class="fu-word"></div>
<!-- sq2图片 -->
<img
v-if="sq2ImageVisible"
src="/static/images/sq2.png"
alt="新春祝福"
class="sq2-image"
/>
<!-- 点击提示 -->
<div class="click-indicator" :class="{ 'animate-pulse': !sealCollected }">
<div class="pulse-circle"></div>
</div>
</div>
<!-- 查看按钮 -->
<img
src="/static/images/btn_view.png"
alt="查看详情"
class="btn-view"
@click="openWebview"
/>
<!-- 交互区域 -->
<div class="interaction-area" @click="playDrum">
<!-- 覆盖在图片上的点击区域 -->
</div>
<!-- 烟花效果 -->
<div class="fireworks">
<div class="firework firework-1">🎆</div>
<div class="firework firework-2">🎇</div>
<div class="firework firework-3">🎆</div>
<div class="firework firework-4">🎇</div>
</div>
<!-- 福印收集标记 -->
<div v-if="sealCollected" class="seal-collected-mark">
<div class="seal-icon">🏮</div>
<div class="seal-text">已收集国潮福字</div>
</div>
</section>
</template>
@ -335,6 +333,34 @@ onMounted(() => {
to { opacity: 1; transform: translateY(0); }
}
/* sq2图片 */
.sq2-image {
position: absolute;
top: 220rpx;
right: -5rpx;
width: auto;
height: auto;
max-width: 300rpx;
z-index: 20;
animation: fadeIn 0.5s ease;
}
/* 查看按钮 */
.btn-view {
position: absolute;
left: 156rpx;
top: 597rpx;
width: 439rpx;
height: 84rpx;
cursor: pointer;
z-index: 25;
transition: all 0.3s ease;
}
.btn-view:active {
transform: scale(0.95);
}
/* 响应式设计 */
@media (max-width: 640px) {
.fu-word {

168
components/FuClickArea.vue Normal file
View File

@ -0,0 +1,168 @@
<script setup>
import { ref, onMounted } from 'vue'
//
const props = defineProps({
//
visible: {
type: Boolean,
default: true
},
// x
xRange: {
type: Number,
default: 630
},
// y
yRange: {
type: Number,
default: 400
},
// y
yStart: {
type: Number,
default: 350
},
//
fuWidth: {
type: Number,
default: 100
},
//
fuHeight: {
type: Number,
default: 100
}
})
//
const emit = defineEmits(['click'])
//
const position = ref({
x: 0,
y: 0
})
//
const calculateRandomPosition = () => {
position.value = {
x: Math.random() * (props.xRange - props.fuWidth),
y: Math.random() * (props.yRange - props.fuHeight) + props.yStart
}
}
//
const handleClick = () => {
emit('click')
}
//
onMounted(() => {
calculateRandomPosition()
})
</script>
<template>
<div
v-if="visible"
class="fu-click-area"
:style="{
left: `${position.x}rpx`,
top: `${position.y}rpx`,
width: `${fuWidth}rpx`,
height: `${fuHeight}rpx`
}"
@click="handleClick"
>
<img src="/static/images/icon_fu.png" alt="福字" class="fu-icon" />
<img src="/static/images/icon_hand.png" alt="点击手势" class="hand-icon" />
<div class="click-indicator">
<div class="pulse-circle"></div>
</div>
</div>
</template>
<style scoped>
/* 福字点击区域 */
.fu-click-area {
position: absolute;
cursor: pointer;
z-index: 25;
pointer-events: auto;
}
.fu-icon {
position: absolute;
width: 94rpx;
height: 74rpx;
top: 0;
left: 50%;
transform: translateX(-50%);
margin-bottom: 20rpx;
animation: sway 3s infinite ease-in-out;
transform-origin: center center;
}
@keyframes sway {
0%, 100% {
transform: translateX(-50%) rotate(0deg);
}
25% {
transform: translateX(-50%) rotate(-3deg);
}
75% {
transform: translateX(-50%) rotate(3deg);
}
}
.hand-icon {
position: absolute;
width: 38rpx;
height: 40rpx;
top: 70%;
left: 50%;
transform: translate(-50%, -50%);
animation: clickUp 2s infinite ease-in-out;
pointer-events: none;
}
@keyframes clickUp {
0%, 100% {
transform: translate(-50%, -50%) scale(1.2);
}
50% {
transform: translate(-50%, -50%) scale(1);
}
}
.click-indicator {
position: absolute;
top: 30rpx;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 60rpx;
}
.pulse-circle {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: rgba(255, 215, 0, 0.3);
border: 2rpx solid rgba(255, 215, 0, 0.6);
animation: pulse 2s infinite;
animation-delay: 1s;
}
@keyframes pulse {
0% {
transform: scale(0.8);
opacity: 0.8;
}
100% {
transform: scale(2);
opacity: 0;
}
}
</style>

View File

@ -1,5 +1,6 @@
<script setup>
import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
import FuClickArea from './FuClickArea.vue'
//
const props = defineProps({
@ -29,12 +30,6 @@ const musicPlayer = ref(null)
const fuClickAreaVisible = ref(true)
const sq1ImageVisible = ref(false)
// y>350630*400100*100
const fuClickPosition = ref({
x: Math.random() * 530, // x: 0-530rpx630rpx - 100rpx
y: Math.random() * 300 + 350 // y: 350-650rpxy>350400rpx - 100rpx
})
//
const sceneHeight = ref(0)
@ -77,30 +72,11 @@ const parallaxOffset = computed(() => {
return offset
})
//
const collectSeal = () => {
if (!sealCollected.value) {
sealCollected.value = true
emit('collect-seal')
showToast({
message: '恭喜获得非遗福印!',
icon: 'success',
duration: 2000
})
}
}
//
const handleFuClick = () => {
if (fuClickAreaVisible.value) {
fuClickAreaVisible.value = false
sq1ImageVisible.value = true
showToast({
message: '恭喜获得新春祝福!',
icon: 'success',
duration: 2000
})
}
emit('collect-seal')
}
// /
@ -207,21 +183,15 @@ onUnmounted(() => {
</div>
<!-- 福字点击区域 -->
<div
v-if="fuClickAreaVisible"
class="fu-click-area"
:style="{
left: `${fuClickPosition.x}rpx`,
top: `${fuClickPosition.y}rpx`
}"
<FuClickArea
:visible="fuClickAreaVisible"
:x-range="630"
:y-range="400"
:y-start="350"
:fu-width="100"
:fu-height="100"
@click="handleFuClick"
>
<img src="/static/images/icon_fu.png" alt="福字" class="fu-icon" />
<img src="/static/images/icon_hand.png" alt="点击手势" class="hand-icon" />
<div class="click-indicator">
<div class="pulse-circle"></div>
</div>
</div>
/>
<!-- sq1图片 -->
<img
@ -480,78 +450,6 @@ onUnmounted(() => {
}
}
/* 福字点击区域 */
.fu-click-area {
position: absolute;
width: 100rpx;
height: 100rpx;
cursor: pointer;
z-index: 25;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
pointer-events: auto;
}
.fu-icon {
width: 94rpx;
height: 74rpx;
margin-bottom: 20rpx;
animation: sway 3s infinite ease-in-out;
transform-origin: center center;
}
@keyframes sway {
0%, 100% {
transform: rotate(0deg);
}
25% {
transform: rotate(-3deg);
}
75% {
transform: rotate(3deg);
}
}
.hand-icon {
position: absolute;
width: 38rpx;
height: 40rpx;
top: 70%;
transform: translate(-50%, -50%);
animation: clickUp 2s infinite ease-in-out;
pointer-events: none;
}
@keyframes clickUp {
0%, 100% {
transform: translateY(0) scale(1.2);
}
50% {
transform: translateY(-10rpx) scale(1);
}
}
.fu-click-area .click-indicator {
position: absolute;
top: 40rpx;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 60rpx;
}
.fu-click-area .pulse-circle {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: rgba(255, 215, 0, 0.3);
border: 2rpx solid rgba(255, 215, 0, 0.6);
animation: pulse 2s infinite;
animation-delay: 1s; /* hand-icon播放到50%1秒后再开始 */
}
/* sq1图片 */
.sq1-image {
position: absolute;

View File

@ -7,12 +7,19 @@
"navigationStyle": "custom",
"disableScroll": true
}
},
{
"path": "pages/webview/webview",
"style": {
"navigationBarTitleText": "四大特色主题区",
"navigationStyle": "default"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "2026新春H5",
"navigationBarBackgroundColor": "#F8F8F8",
"navigationBarBackgroundColor": "#fa4333",
"backgroundColor": "#F8F8F8"
},
"uniIdRouter": {}

33
pages/webview/webview.vue Normal file
View File

@ -0,0 +1,33 @@
<template>
<view class="webview-container">
<web-view :src="url"></web-view>
</view>
</template>
<script setup>
import { ref } from 'vue'
const url = ref('')
//
onLoad((options) => {
if (options && options.url) {
// URL
url.value = decodeURIComponent(options.url)
console.log('Webview loading URL:', url.value)
} else {
// URL
url.value = 'https://www.720yun.com/t/1dvktq8b0fl?scene_id=74010726'
}
})
// onLoad @dcloudio/uni-app
import { onLoad } from '@dcloudio/uni-app'
</script>
<style scoped>
.webview-container {
width: 100%;
height: 100vh;
}
</style>