feat: 添加背景滚动功能
This commit is contained in:
parent
6d36f308a3
commit
7dd947ca12
@ -75,7 +75,7 @@ const props = defineProps({
|
|||||||
isActive: { type: Boolean, default: true }, // 是否处于激活状态
|
isActive: { type: Boolean, default: true }, // 是否处于激活状态
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['cardClick'])
|
const emit = defineEmits(['cardClick', 'scroll'])
|
||||||
|
|
||||||
// ========== 布局常量 ==========
|
// ========== 布局常量 ==========
|
||||||
const rpx2px = (rpx) => Math.round(uni.getSystemInfoSync().windowWidth / 750 * rpx)
|
const rpx2px = (rpx) => Math.round(uni.getSystemInfoSync().windowWidth / 750 * rpx)
|
||||||
@ -121,18 +121,44 @@ const iosScrollPaused = ref(true)
|
|||||||
const startIOSAutoScroll = () => {
|
const startIOSAutoScroll = () => {
|
||||||
if (!isComponentMounted || !isIOS) return
|
if (!isComponentMounted || !isIOS) return
|
||||||
iosScrollPaused.value = false
|
iosScrollPaused.value = false
|
||||||
|
|
||||||
|
// 启动 iOS 滚动位置追踪,定时发送 scrollLeft 给父组件
|
||||||
|
clearInterval(iosScrollEmitTimer)
|
||||||
|
iosScrollEmitTimer = setInterval(() => {
|
||||||
|
if (!isComponentMounted) return
|
||||||
|
// iOS CSS 动画从 0 到 totalWidth.value,循环播放
|
||||||
|
const scrollDist = totalWidth.value
|
||||||
|
const duration = scrollDist / AUTO_SCROLL_SPEED_IOS
|
||||||
|
const elapsed = (Date.now() % duration)
|
||||||
|
const pos = (elapsed / duration) * scrollDist
|
||||||
|
emit('scroll', pos)
|
||||||
|
}, 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
const stopIOSAutoScroll = () => {
|
const stopIOSAutoScroll = () => {
|
||||||
iosScrollPaused.value = true
|
iosScrollPaused.value = true
|
||||||
|
clearInterval(iosScrollEmitTimer)
|
||||||
|
iosScrollEmitTimer = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const pauseIOSAutoScroll = () => {
|
const pauseIOSAutoScroll = () => {
|
||||||
iosScrollPaused.value = true
|
iosScrollPaused.value = true
|
||||||
|
clearInterval(iosScrollEmitTimer)
|
||||||
|
iosScrollEmitTimer = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const resumeIOSAutoScroll = () => {
|
const resumeIOSAutoScroll = () => {
|
||||||
iosScrollPaused.value = false
|
iosScrollPaused.value = false
|
||||||
|
// 重新启动 iOS 滚动位置追踪
|
||||||
|
clearInterval(iosScrollEmitTimer)
|
||||||
|
iosScrollEmitTimer = setInterval(() => {
|
||||||
|
if (!isComponentMounted) return
|
||||||
|
const scrollDist = totalWidth.value
|
||||||
|
const duration = scrollDist / AUTO_SCROLL_SPEED_IOS
|
||||||
|
const elapsed = (Date.now() % duration)
|
||||||
|
const pos = (elapsed / duration) * scrollDist
|
||||||
|
emit('scroll', pos)
|
||||||
|
}, 16)
|
||||||
}
|
}
|
||||||
let rafId = null
|
let rafId = null
|
||||||
let userInteracting = false
|
let userInteracting = false
|
||||||
@ -141,6 +167,8 @@ let appendTimer = null // 防抖定时器
|
|||||||
let autoScrollPos = 0 // 自动滚动目标位置
|
let autoScrollPos = 0 // 自动滚动目标位置
|
||||||
let momentumTimer = null // iOS/Android 惯性滚动检测定时器
|
let momentumTimer = null // iOS/Android 惯性滚动检测定时器
|
||||||
let scrollUpdateTimer = null // 定期同步 scrollLeft 的定时器(仅 Android)
|
let scrollUpdateTimer = null // 定期同步 scrollLeft 的定时器(仅 Android)
|
||||||
|
let iosScrollEmitTimer = null // iOS 滚动位置发射定时器
|
||||||
|
let iosCurrentScrollPos = 0 // iOS CSS 动画当前滚动位置
|
||||||
|
|
||||||
const startAutoScroll = () => {
|
const startAutoScroll = () => {
|
||||||
if (!isComponentMounted || isIOS) return
|
if (!isComponentMounted || isIOS) return
|
||||||
@ -269,6 +297,9 @@ const onScroll = (e) => {
|
|||||||
if (!isComponentMounted) return
|
if (!isComponentMounted) return
|
||||||
currentScrollLeft = e.detail.scrollLeft
|
currentScrollLeft = e.detail.scrollLeft
|
||||||
|
|
||||||
|
// 发送滚动事件给父组件用于背景视差
|
||||||
|
emit('scroll', currentScrollLeft)
|
||||||
|
|
||||||
// iOS 手动滚动时,不再更新自动滚动相关变量
|
// iOS 手动滚动时,不再更新自动滚动相关变量
|
||||||
if (isIOS && isManualScrolling) {
|
if (isIOS && isManualScrolling) {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -1,7 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="square-container">
|
<view class="square-container">
|
||||||
<!-- 固定背景 -->
|
<!-- 可横向滑动的背景 -->
|
||||||
<image class="background-fixed" src="/static/square/squearbj.png" mode="aspectFill" />
|
<view class="bg-wrapper">
|
||||||
|
<image
|
||||||
|
class="background-fixed"
|
||||||
|
:style="{ transform: `translateX(${-bgScrollLeft}px)` }"
|
||||||
|
src="/static/square/squearbj1.png"
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 横向瀑布流卡片层(内部自带横向滚动) -->
|
<!-- 横向瀑布流卡片层(内部自带横向滚动) -->
|
||||||
<WaterfallGrid
|
<WaterfallGrid
|
||||||
@ -13,6 +20,7 @@
|
|||||||
:category="activeContentTab"
|
:category="activeContentTab"
|
||||||
:isActive="isActive"
|
:isActive="isActive"
|
||||||
@cardClick="handleCardClick"
|
@cardClick="handleCardClick"
|
||||||
|
@scroll="handleWaterfallScroll"
|
||||||
class="fall-bg"
|
class="fall-bg"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -105,11 +113,19 @@ const {
|
|||||||
// banner(216+360rpx) + tab栏(16+80rpx) + 间距(8rpx) ≈ 680rpx
|
// banner(216+360rpx) + tab栏(16+80rpx) + 间距(8rpx) ≈ 680rpx
|
||||||
const bannerBottomPx = computed(() => Math.round(screenWidth.value / 750 * 715))
|
const bannerBottomPx = computed(() => Math.round(screenWidth.value / 750 * 715))
|
||||||
|
|
||||||
|
const bgScrollLeft = ref(0)
|
||||||
|
|
||||||
// ========== Handlers ==========
|
// ========== Handlers ==========
|
||||||
|
|
||||||
const handleCardClick = (card) => {
|
const handleCardClick = (card) => {
|
||||||
// WaterfallGrid 组件内部已处理单击跳转和双击点赞
|
// WaterfallGrid 组件内部已处理单击跳转和双击点赞
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleWaterfallScroll = (scrollLeft) => {
|
||||||
|
// 背景以 30% 速度跟随瀑布流滚动
|
||||||
|
bgScrollLeft.value = scrollLeft * 0.3
|
||||||
|
}
|
||||||
|
|
||||||
const handleActivityClick = (item) => {
|
const handleActivityClick = (item) => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/support-activity/index?id=${item.id}`,
|
url: `/pages/support-activity/index?id=${item.id}`,
|
||||||
@ -260,13 +276,19 @@ onUnmounted(() => {
|
|||||||
margin-bottom: 8rpx;
|
margin-bottom: 8rpx;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
.background-fixed {
|
.bg-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 110%;
|
height: 110%;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-fixed {
|
||||||
|
width: 300%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-mask {
|
.nav-mask {
|
||||||
|
|||||||
BIN
frontend/static/square/squearbj1.png
Normal file
BIN
frontend/static/square/squearbj1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 MiB |
Loading…
Reference in New Issue
Block a user