v1.2.4
1、增加loading组件,用于预加载scroll-view的背景图片,然后使得scroll-view进行隐藏的滚动到首页 2、调整首页滚动逻辑,使得可以完全屏蔽滚动;同时修改向上滚动逻辑
This commit is contained in:
parent
ab75f0056c
commit
e3d0ffe1e6
|
|
@ -0,0 +1,216 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
|
||||
// 组件属性
|
||||
const props = defineProps({
|
||||
// 是否显示
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
// 组件事件
|
||||
const emit = defineEmits(['loaded', 'start'])
|
||||
|
||||
// 加载进度
|
||||
const loadingProgress = ref(0)
|
||||
// 是否加载完成
|
||||
const isLoadingComplete = ref(false)
|
||||
// 是否显示开始按钮
|
||||
const showStartButton = ref(false)
|
||||
|
||||
// 生成带时间戳的图片URL(避免缓存)
|
||||
const generateImageUrl = (name) => {
|
||||
const url = new URL(`/static/bg/${name}.jpg`, import.meta.url)
|
||||
// 添加时间戳参数避免浏览器缓存
|
||||
return url.href
|
||||
}
|
||||
|
||||
// 计算开始按钮是否可见
|
||||
const startButtonVisible = computed(() => {
|
||||
return isLoadingComplete.value && showStartButton.value
|
||||
})
|
||||
|
||||
// 加载图片函数
|
||||
const loadImages = async () => {
|
||||
// 图片名称数组
|
||||
const bgImageNames = [
|
||||
'bg1',
|
||||
'bg2',
|
||||
'bg3',
|
||||
'bg4',
|
||||
'bg5',
|
||||
'bg_finish',
|
||||
'bg_main'
|
||||
]
|
||||
|
||||
// 生成图片URL数组
|
||||
const bgImages = bgImageNames.map(name => generateImageUrl(name))
|
||||
|
||||
const totalImages = bgImages.length
|
||||
let loadedImages = 0
|
||||
|
||||
// 加载每张图片
|
||||
for (const imagePath of bgImages) {
|
||||
try {
|
||||
await loadImage(imagePath)
|
||||
loadedImages++
|
||||
// 更新加载进度
|
||||
const progress = Math.round((loadedImages / totalImages) * 100)
|
||||
loadingProgress.value = progress
|
||||
|
||||
// 模拟加载延迟,使进度条动画更流畅
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
} catch (error) {
|
||||
console.error('加载图片失败:', imagePath, error)
|
||||
// 即使加载失败,也继续加载其他图片
|
||||
loadedImages++
|
||||
loadingProgress.value = Math.round((loadedImages / totalImages) * 100)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载完成
|
||||
isLoadingComplete.value = true
|
||||
loadingProgress.value = 100
|
||||
|
||||
// 延迟显示开始按钮,让用户看到100%的进度
|
||||
setTimeout(() => {
|
||||
showStartButton.value = true
|
||||
// 通知父组件加载完成
|
||||
emit('loaded')
|
||||
}, 500)
|
||||
}
|
||||
|
||||
// 单个图片加载函数
|
||||
const loadImage = (src) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image()
|
||||
img.onload = resolve
|
||||
img.onerror = reject
|
||||
img.src = src
|
||||
})
|
||||
}
|
||||
|
||||
// 点击开始按钮
|
||||
const handleStart = () => {
|
||||
emit('start')
|
||||
}
|
||||
|
||||
// 组件挂载后开始加载图片
|
||||
onMounted(() => {
|
||||
loadImages()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="visible" class="loading-container">
|
||||
<!-- 背景图片 -->
|
||||
<div class="loading-bg">
|
||||
<img src="/static/loading/loading_bg.jpg" alt="加载背景" class="bg-image" />
|
||||
</div>
|
||||
|
||||
<!-- 进度条区域 -->
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: `${loadingProgress}%` }"></div>
|
||||
</div>
|
||||
<div class="progress-text">{{ loadingProgress }}%</div>
|
||||
</div>
|
||||
|
||||
<!-- 开始按钮 -->
|
||||
<div v-if="startButtonVisible" class="start-button" @click="handleStart">
|
||||
<img src="/static/loading/btn_start.png" alt="开始" class="start-btn-image" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.loading-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.loading-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.bg-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
position: absolute;
|
||||
bottom: 30vh;
|
||||
width: 80%;
|
||||
max-width: 500rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 20rpx;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background-color: #FFD700;
|
||||
border-radius: 10rpx;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #FFD700;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.start-button {
|
||||
position: absolute;
|
||||
bottom: 20vh;
|
||||
width: 300rpx;
|
||||
height: 100rpx;
|
||||
cursor: pointer;
|
||||
animation: pulse 1.5s infinite;
|
||||
}
|
||||
|
||||
.start-btn-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -14,6 +14,7 @@ import EndPage from './EndPage.vue'
|
|||
import LotteryFormModal from './LotteryFormModal.vue'
|
||||
import AICoupletForm from './AICoupletForm.vue'
|
||||
import CoupletDisplay from './CoupletDisplay.vue'
|
||||
import LoadingComponent from './LoadingComponent.vue'
|
||||
|
||||
const sceneStore = useSceneStore()
|
||||
const collectionStore = useCollectionStore()
|
||||
|
|
@ -52,6 +53,10 @@ const hasSubmittedUserInfo = ref(false)
|
|||
const hasScrolled = ref(false)
|
||||
// 标记是否正在自动滚动
|
||||
const isAutoScrolling = ref(false)
|
||||
// 是否显示加载组件
|
||||
const showLoading = ref(true)
|
||||
// 是否已完成图片加载
|
||||
const isImagesLoaded = ref(false)
|
||||
|
||||
|
||||
// 推荐关键词
|
||||
|
|
@ -140,8 +145,95 @@ const collectedSeals = computed(() => {
|
|||
}
|
||||
})
|
||||
|
||||
// 组件挂载后初始化
|
||||
onMounted(() => {
|
||||
// 处理加载完成事件
|
||||
const handleLoadingComplete = () => {
|
||||
isImagesLoaded.value = true
|
||||
console.log('图片加载完成,准备初始化页面')
|
||||
|
||||
// 确保 scrollContainer 已初始化并滚动到最下方
|
||||
ensureScrollContainerReady()
|
||||
}
|
||||
|
||||
// 确保 scrollContainer 已初始化并滚动到最下方
|
||||
const ensureScrollContainerReady = () => {
|
||||
// 等待 DOM 渲染完成
|
||||
nextTick(() => {
|
||||
const checkScrollContainer = () => {
|
||||
if (!scrollContainer.value) {
|
||||
console.log('scrollContainer 尚未初始化,延迟重试...')
|
||||
setTimeout(checkScrollContainer, 100)
|
||||
return
|
||||
}
|
||||
|
||||
const container = scrollContainer.value
|
||||
console.log('scrollContainer 已初始化:', {
|
||||
scrollHeight: container.scrollHeight,
|
||||
clientHeight: container.clientHeight
|
||||
})
|
||||
|
||||
// 如果 scrollHeight 为 0,说明内容还未渲染
|
||||
if (container.scrollHeight === 0) {
|
||||
console.log('内容尚未渲染,延迟重试...')
|
||||
setTimeout(checkScrollContainer, 100)
|
||||
return
|
||||
}
|
||||
|
||||
// 标记正在自动滚动
|
||||
isAutoScrolling.value = true
|
||||
|
||||
// 设置首页 section ID,使用 scroll-into-view 滚动到首页
|
||||
homeSectionId.value = 'home-section'
|
||||
console.log('使用 scroll-into-view 滚动到首页')
|
||||
|
||||
// 设置活动场景为首页
|
||||
activeSceneIndex.value = scenes.value.length - 1
|
||||
|
||||
// 等待 scroll-into-view 生效
|
||||
setTimeout(() => {
|
||||
// 再次检查并滚动到最下方,确保滚动位置正确
|
||||
try {
|
||||
const targetScrollTop = container.scrollHeight - container.clientHeight
|
||||
if (typeof container.scrollTo === 'function') {
|
||||
container.scrollTo({
|
||||
top: targetScrollTop,
|
||||
duration: 0
|
||||
})
|
||||
} else {
|
||||
container.scrollTop = targetScrollTop
|
||||
}
|
||||
console.log('已滚动到最下方,scrollTop:', container.scrollTop)
|
||||
} catch (error) {
|
||||
console.error('滚动失败:', error)
|
||||
}
|
||||
|
||||
// 自动滚动结束
|
||||
isAutoScrolling.value = false
|
||||
// 强制将 hasScrolled 设置为 false,确保滑动提示能够显示
|
||||
hasScrolled.value = false
|
||||
console.log('scrollContainer 准备就绪,hasScrolled:', hasScrolled.value)
|
||||
}, 300)
|
||||
}
|
||||
|
||||
// 开始检查 scrollContainer
|
||||
checkScrollContainer()
|
||||
})
|
||||
}
|
||||
|
||||
// 处理开始按钮点击事件
|
||||
const handleStart = () => {
|
||||
// 隐藏加载组件
|
||||
showLoading.value = false
|
||||
|
||||
// 确保滑动提示能够显示
|
||||
hasScrolled.value = false
|
||||
console.log('点击开始按钮,hasScrolled:', hasScrolled.value)
|
||||
|
||||
// 初始化页面
|
||||
initPage()
|
||||
}
|
||||
|
||||
// 初始化页面函数
|
||||
const initPage = () => {
|
||||
// 记录页面访问
|
||||
recordPageVisit({
|
||||
user_agent: navigator.userAgent,
|
||||
|
|
@ -163,114 +255,15 @@ onMounted(() => {
|
|||
}
|
||||
})
|
||||
|
||||
// 添加滚动事件监听 - 使用uniapp的scroll-view事件绑定
|
||||
// scroll-view 组件通过 @scroll 事件监听滚动,无需手动添加事件监听器
|
||||
// 标记页面准备就绪
|
||||
titleImageShown.value = true
|
||||
isAutoScrolling.value = false
|
||||
console.log('页面准备就绪')
|
||||
}
|
||||
|
||||
|
||||
// 页面加载时滚动到最底部(首页)
|
||||
const scrollToHomePage = () => {
|
||||
// 标记正在自动滚动
|
||||
isAutoScrolling.value = true
|
||||
|
||||
if (!scrollContainer.value) {
|
||||
console.log('scrollContainer 尚未初始化')
|
||||
return
|
||||
}
|
||||
|
||||
// 保存当前容器引用,避免异步回调中丢失
|
||||
const container = scrollContainer.value
|
||||
|
||||
console.log('scrollContainer 已初始化:', {
|
||||
element: container,
|
||||
scrollHeight: container.scrollHeight,
|
||||
clientHeight: container.clientHeight,
|
||||
scrollTop: container.scrollTop,
|
||||
classList: container.classList
|
||||
})
|
||||
|
||||
// 如果scrollHeight为0,说明内容还未渲染
|
||||
if (container.scrollHeight === 0) {
|
||||
console.log('内容尚未渲染,延迟重试...')
|
||||
setTimeout(scrollToHomePage, 100)
|
||||
return
|
||||
}
|
||||
|
||||
// 首页是最后一个场景,索引为 scenes.value.length - 1
|
||||
// 应该位于页面最底部
|
||||
const homeIndex = scenes.value.length - 1
|
||||
|
||||
// 方法1: 使用 scroll-into-view(uniapp 原生支持)
|
||||
console.log('使用 scroll-into-view 滚动到首页')
|
||||
homeSectionId.value = 'home-section'
|
||||
|
||||
// 设置活动场景为首页
|
||||
activeSceneIndex.value = homeIndex
|
||||
|
||||
// 设置活动场景为首页
|
||||
activeSceneIndex.value = homeIndex
|
||||
|
||||
// 验证滚动是否成功,如果不成功,延迟后重试
|
||||
setTimeout(() => {
|
||||
if (!container) {
|
||||
console.log('scrollContainer 已卸载')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('验证滚动结果:', {
|
||||
currentScrollTop: container.scrollTop,
|
||||
scrollHeight: container.scrollHeight,
|
||||
clientHeight: container.clientHeight,
|
||||
isAtBottom: Math.abs(container.scrollTop - (container.scrollHeight - container.clientHeight)) < 50
|
||||
})
|
||||
|
||||
// 检查是否已经滚动到底部
|
||||
const isAtBottom = Math.abs(container.scrollTop - (container.scrollHeight - container.clientHeight)) < 50
|
||||
|
||||
if (!isAtBottom) {
|
||||
console.log('滚动验证失败,使用备用方法重试...')
|
||||
// 方法2: 使用 scrollTo 或 scrollTop
|
||||
try {
|
||||
const targetScrollTop = container.scrollHeight - container.clientHeight
|
||||
if (typeof container.scrollTo === 'function') {
|
||||
container.scrollTo({
|
||||
top: targetScrollTop,
|
||||
duration: 0
|
||||
})
|
||||
} else {
|
||||
container.scrollTop = targetScrollTop
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('备用方法失败:', error)
|
||||
container.scrollTop = container.scrollHeight - container.clientHeight
|
||||
}
|
||||
|
||||
// 再次验证后显示容器
|
||||
setTimeout(() => {
|
||||
// 标记页面准备就绪,隐藏加载提示并显示页面
|
||||
isPageReady.value = true
|
||||
titleImageShown.value = true
|
||||
// 自动滚动结束
|
||||
isAutoScrolling.value = false
|
||||
console.log('设置 isPageReady = true,准备显示页面')
|
||||
}, 50)
|
||||
} else {
|
||||
console.log('成功滚动到首页')
|
||||
// 滚动成功后显示容器
|
||||
// 标记页面准备就绪,隐藏加载提示并显示页面
|
||||
isPageReady.value = true
|
||||
titleImageShown.value = true
|
||||
// 自动滚动结束
|
||||
isAutoScrolling.value = false
|
||||
console.log('设置 isPageReady = true,准备显示页面')
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
|
||||
// 初始滚动到首页 - 延迟更长时间确保内容渲染
|
||||
setTimeout(scrollToHomePage, 800)
|
||||
|
||||
// 额外的确保措施,等待更长时间后再次尝试
|
||||
setTimeout(scrollToHomePage, 1500)
|
||||
// 组件挂载后初始化
|
||||
onMounted(() => {
|
||||
console.log('SinglePageContainer 组件挂载')
|
||||
})
|
||||
|
||||
// 处理滚动事件(从下往上滑动)
|
||||
|
|
@ -512,15 +505,15 @@ onUnmounted(() => {
|
|||
|
||||
<template>
|
||||
<div class="single-page-wrapper">
|
||||
<!-- 加载提示 -->
|
||||
<div class="loading-hint" v-if="!isPageReady">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>正在加载...</p>
|
||||
</div>
|
||||
<!-- 加载组件 -->
|
||||
<LoadingComponent
|
||||
v-if="showLoading"
|
||||
@loaded="handleLoadingComplete"
|
||||
@start="handleStart"
|
||||
/>
|
||||
|
||||
<scroll-view
|
||||
class="single-page-container"
|
||||
:class="{ 'visible': isPageReady }"
|
||||
ref="scrollContainer"
|
||||
scroll-y="true"
|
||||
scroll-with-animation="true"
|
||||
|
|
@ -654,44 +647,6 @@ onUnmounted(() => {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 加载提示样式 */
|
||||
.loading-hint {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
background: rgba(253, 233, 223, 0.95);
|
||||
padding: 30px 40px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid #FF6B35;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.loading-hint p {
|
||||
color: #FF6B35;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.single-page-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
|
@ -699,17 +654,11 @@ onUnmounted(() => {
|
|||
overflow-y: scroll; /* 改为 scroll 确保可以滚动 */
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch; /* 启用iOS平滑滚动 */
|
||||
/* 使用 visibility 而不是 opacity,确保完全不可见且不可交互 */
|
||||
visibility: hidden;
|
||||
touch-action: pan-y; /* 允许垂直方向的触摸滚动 */
|
||||
user-select: none; /* 防止触摸时文本被选中 */
|
||||
}
|
||||
|
||||
.single-page-container.visible {
|
||||
visibility: visible;
|
||||
/* 添加淡入效果 */
|
||||
animation: fadeIn 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue