feat: 修复首页滚动卡顿并添加下拉刷新功能
滚动性能优化: - 使用ScrollView组件替代普通View,提供原生滚动性能 - 启用硬件加速:transform: translateZ(0)和will-change属性 - 优化CSS动画:减少clip-path动画频率,简化transition效果 - 图片渲染优化:image-rendering: optimizeSpeed和backface-visibility 下拉刷新功能: - 集成ScrollView原生下拉刷新能力 - 添加refresherEnabled、refresherTriggered等配置 - 实现handleRefresh异步刷新逻辑 - 完善错误处理和用户反馈 性能提升措施: - 移除不必要的transition动画减少重绘 - 使用requestAnimationFrame节流触摸事件 - 启用contain: layout style paint优化渲染 - 简化active状态效果,使用opacity替代transform 用户体验改进: - 流畅的原生滚动体验,消除卡顿现象 - 直观的下拉刷新操作,符合用户习惯 - 完整的加载状态和错误提示 - 自动数据同步和状态管理 技术实现: - ScrollView enhanced模式启用增强特性 - enablePassive被动事件监听提升性能 - refresherBackground和refresherDefaultStyle视觉定制 - 异步错误处理和Toast提示集成 解决问题:首页滚动时的卡顿现象,提升用户交互体验
This commit is contained in:
parent
77afadbe36
commit
4c0d69e1b2
|
|
@ -3,7 +3,7 @@
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
transition: all 0.3s ease;
|
/* 优化:移除transition,减少重绘 */
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -12,11 +12,14 @@
|
||||||
break-inside: avoid;
|
break-inside: avoid;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
/* 性能优化:启用硬件加速 */
|
||||||
|
transform: translateZ(0);
|
||||||
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 优化:简化active状态,减少重绘 */
|
||||||
.template-card:active {
|
.template-card:active {
|
||||||
transform: translateY(-2px);
|
opacity: 0.9;
|
||||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 合成对比图片区域 */
|
/* 合成对比图片区域 */
|
||||||
|
|
@ -46,8 +49,12 @@
|
||||||
min-height: 240px;
|
min-height: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 优化:减少clip-path动画频率,提升性能 */
|
||||||
.overlay-layer {
|
.overlay-layer {
|
||||||
transition: clip-path 0.1s ease-out;
|
transition: clip-path 0.2s ease-out;
|
||||||
|
/* 启用硬件加速 */
|
||||||
|
transform: translateZ(0);
|
||||||
|
will-change: clip-path;
|
||||||
}
|
}
|
||||||
|
|
||||||
.full-image {
|
.full-image {
|
||||||
|
|
@ -56,6 +63,11 @@
|
||||||
min-height: 240px;
|
min-height: 240px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
display: block;
|
display: block;
|
||||||
|
/* 性能优化:启用硬件加速和图片优化 */
|
||||||
|
transform: translateZ(0);
|
||||||
|
image-rendering: optimizeSpeed;
|
||||||
|
/* 防止图片闪烁 */
|
||||||
|
backface-visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export default function TemplateCard({ template, onClick }: TemplateCardProps) {
|
||||||
await getContainerInfo()
|
await getContainerInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理触摸移动
|
// 处理触摸移动 - 优化:添加节流,减少计算频率
|
||||||
const handleTouchMove = (e: any) => {
|
const handleTouchMove = (e: any) => {
|
||||||
if (!isDragging || !containerInfo) return
|
if (!isDragging || !containerInfo) return
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
@ -51,11 +51,14 @@ export default function TemplateCard({ template, onClick }: TemplateCardProps) {
|
||||||
const touch = e.touches[0]
|
const touch = e.touches[0]
|
||||||
if (!touch) return
|
if (!touch) return
|
||||||
|
|
||||||
// 计算触摸点相对于容器的位置
|
// 使用requestAnimationFrame节流,提升性能
|
||||||
const touchX = touch.clientX - containerInfo.left
|
requestAnimationFrame(() => {
|
||||||
const percentage = Math.max(10, Math.min(90, (touchX / containerInfo.width) * 100))
|
// 计算触摸点相对于容器的位置
|
||||||
|
const touchX = touch.clientX - containerInfo.left
|
||||||
|
const percentage = Math.max(10, Math.min(90, (touchX / containerInfo.width) * 100))
|
||||||
|
|
||||||
setSplitPosition(percentage)
|
setSplitPosition(percentage)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理触摸结束
|
// 处理触摸结束
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,18 @@
|
||||||
.home {
|
.home {
|
||||||
padding: 16px 12px 20px;
|
|
||||||
background: linear-gradient(180deg, #f8f9fa 0%, #e9ecef 100%);
|
background: linear-gradient(180deg, #f8f9fa 0%, #e9ecef 100%);
|
||||||
min-height: 100vh;
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ScrollView 容器样式 */
|
||||||
|
.home-scroll {
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
padding: 16px 12px 20px;
|
||||||
|
/* 滚动性能优化 */
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-header {
|
.home-header {
|
||||||
|
|
@ -37,6 +48,8 @@
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
|
/* 最小高度确保内容撑开 */
|
||||||
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 响应式调整 */
|
/* 响应式调整 */
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { View, Text } from '@tarojs/components'
|
import { View, Text, ScrollView } from '@tarojs/components'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import Taro, { navigateTo } from '@tarojs/taro'
|
import Taro, { navigateTo } from '@tarojs/taro'
|
||||||
import { useAppDispatch, useAppSelector } from '../../hooks/redux'
|
import { useAppDispatch, useAppSelector } from '../../hooks/redux'
|
||||||
|
|
@ -16,10 +16,37 @@ export default function Home() {
|
||||||
const sdk = useSdk()
|
const sdk = useSdk()
|
||||||
const serverSdk = useServerSdk()
|
const serverSdk = useServerSdk()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [refreshing, setRefreshing] = useState(false)
|
||||||
|
|
||||||
const loadTemplates = async () => {
|
const loadTemplates = async () => {
|
||||||
const templates = await serverSdk.getAllTemplates()
|
try {
|
||||||
dispatch(initTemplates(templates))
|
const templates = await serverSdk.getAllTemplates()
|
||||||
|
dispatch(initTemplates(templates))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载模板失败:', error)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '加载模板失败',
|
||||||
|
icon: 'error',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下拉刷新处理
|
||||||
|
const handleRefresh = async () => {
|
||||||
|
setRefreshing(true)
|
||||||
|
try {
|
||||||
|
await loadTemplates()
|
||||||
|
Taro.showToast({
|
||||||
|
title: '刷新成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('刷新失败:', error)
|
||||||
|
} finally {
|
||||||
|
setRefreshing(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -103,15 +130,30 @@ export default function Home() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='home'>
|
<View className='home'>
|
||||||
<View className='template-grid'>
|
<ScrollView
|
||||||
{templates.map((template) => (
|
className='home-scroll'
|
||||||
<TemplateCard
|
scrollY
|
||||||
key={template.code}
|
enhanced
|
||||||
template={template}
|
showScrollbar={false}
|
||||||
onClick={handleTemplateClick}
|
enablePassive
|
||||||
/>
|
bounces={true}
|
||||||
))}
|
scrollWithAnimation={false}
|
||||||
</View>
|
refresherEnabled
|
||||||
|
refresherTriggered={refreshing}
|
||||||
|
onRefresherRefresh={handleRefresh}
|
||||||
|
refresherBackground='#f8f9fa'
|
||||||
|
refresherDefaultStyle='black'
|
||||||
|
>
|
||||||
|
<View className='template-grid'>
|
||||||
|
{templates.map((template) => (
|
||||||
|
<TemplateCard
|
||||||
|
key={template.code}
|
||||||
|
template={template}
|
||||||
|
onClick={handleTemplateClick}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue