当前位置: 首页 > news >正文

页面滚动加载更多

父页面 - HomePage

<template><div id="home-page" ref="contentRef"><PictureList ref="pictureListRef" /></div>
</template><script setup lang="ts">
import PictureList from '@/components/PictureList.vue'
import { onMounted, onUnmounted, ref } from 'vue'const pictureListRef = ref()
const contentRef = ref()let lastScrollTop = 0// 找到真正有滚动条的元素
const findScrollableElement = () => {let current = contentRef.valuewhile (current && current !== document.body) {current = current.parentElementif (current && current.scrollHeight > current.clientHeight) {// console.log('找到可滚动的父元素:', current.tagName)return current}}return document.documentElement.scrollHeight > document.body.scrollHeight? document.documentElement: document.body
}const checkScroll = () => {// 直接使用 PictureList 组件的状态来判断if (!pictureListRef.value) returnconst { moreLoading, noMore } = pictureListRef.value// 如果正在加载或没有更多数据,不再触发if (moreLoading || noMore) {return}const scrollElement = findScrollableElement()const scrollTop = scrollElement.scrollTopconst clientHeight = scrollElement.clientHeightconst scrollHeight = scrollElement.scrollHeight// 判断滚动方向(只有向下滚动才触发)const isScrollingDown = scrollTop > lastScrollToplastScrollTop = scrollTop// 计算距离底部的距离const distanceFromBottom = scrollHeight - (scrollTop + clientHeight)// 设置触发阈值,当距离底部 200px 时触发const threshold = 200// console.log('滚动检测:', {//   distanceFromBottom,//   isScrollingDown,//   moreLoading,//   noMore,// })// 只有向下滚动且接近底部时才触发加载if (isScrollingDown && distanceFromBottom <= threshold) {// console.log('触发加载更多')pictureListRef.value.loadMore()}
}// 防抖处理
let scrollTimer: number | null = null
const handleScroll = () => {if (scrollTimer) clearTimeout(scrollTimer)scrollTimer = setTimeout(checkScroll, 150)
}onMounted(() => {setTimeout(() => {const scrollElement = findScrollableElement()// console.log('最终绑定的滚动元素:', scrollElement)scrollElement.addEventListener('scroll', handleScroll)}, 100)
})onUnmounted(() => {const scrollElement = findScrollableElement()if (scrollElement && scrollElement.removeEventListener) {scrollElement.removeEventListener('scroll', handleScroll)}if (scrollTimer) clearTimeout(scrollTimer)
})
</script><style scoped></style>

子组件 - PictureList

<template><div class="picture-list"><a-list:grid="{ gutter: 16, xs: 1, sm: 2, md: 4, lg: 4, xl: 4, xxl: 6 }":data-source="dataList":loading="initLoading"><template #renderItem="{ item }"><a-list-item style="padding: 0"><div class="item"><img :src="item.url" :alt="item.name" loading="lazy" /><div class="tags-container"><a-tag class="tag" v-for="(tag, index) in item.tags" :key="index" color="#69B74D">{{ tag }}</a-tag></div></div></a-list-item></template></a-list><!-- 加载状态 --><div style="text-align: center"><a-spin :spinning="moreLoading" /></div><!-- 没有更多数据提示 --><div v-if="noMore" style="text-align: center; padding: 20px; color: #999">没有更多图片了</div></div>
</template>
<script setup lang="ts">
import { pagePictureVoUsingPost } from '@/api/tupianjiekou.ts'
import { onMounted, reactive, ref } from 'vue'
import { message } from 'ant-design-vue'// 数据列表
const dataList = ref<API.PictureVO[] | undefined>([])
// 初始化加载状态
const initLoading = ref<boolean>(false)
// 加载更多加载状态
const moreLoading = ref<boolean>(false)
// 是否还有更多数据
const noMore = ref<boolean>(false)// 搜索条件
const searchParams = reactive<API.PictureQueryRequest>({current: 1,size: 12,
})// 获取图片列表
const fetchData = async (isLoadMore: boolean = false) => {if (isLoadMore) {moreLoading.value = true} else {initLoading.value = true}try {const res = await pagePictureVoUsingPost({...searchParams,})const { records, current, pages } = res.data.dataif (res.data.code == 0 && res.data.data) {if (isLoadMore) {dataList.value = [...dataList.value, ...records]moreLoading.value = false} else {dataList.value = recordsinitLoading.value = false}noMore.value = current >= pages || records.length === 0} else {message.error(res.data.message || '获取数据失败')}} catch (e) {console.error('获取数据失败:', e)message.error('获取数据失败')} finally {initLoading.value = falsemoreLoading.value = false}
}// 加载更多 - 暴露给父组件
const loadMore = async () => {if (moreLoading.value || noMore.value) returnif (searchParams.current) {searchParams.current++}await fetchData(true)
}// 重置搜索
const resetSearch = () => {searchParams.current = 1noMore.value = falsedataList.value = []fetchData(false)
}// 组件挂载时初始化
onMounted(() => {fetchData()
})// 暴露方法给父组件
defineExpose({loadMore,resetSearch,fetchData,
})
</script><style scoped>
.picture-list {.item {position: relative;height: 30vh;border-radius: 10px;overflow: hidden;transition: all 0.5s ease;&:hover {box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);}img {width: 100%;height: 100%;display: block;object-fit: cover; /* 关键属性:填充整个容器,可能会裁剪图片 */object-position: center; /* 图片居中显示 */}.tags-container {position: absolute;bottom: 10px;left: 10px;display: flex;gap: 6px; /* 标签间距 */flex-wrap: wrap; /* 超出换行 */.tag {bottom: 20px;left: 20px;}}}
}
</style>
http://www.dtcms.com/a/523504.html

相关文章:

  • 除了provide和inject,Vue3还有哪些用于组件通信的方式?
  • React 表单与事件
  • AI代码开发宝库系列:FAISS向量数据库
  • 前端埋点学习
  • Spring AI与DeepSeek实战:打造企业级知识库+系统API调用
  • 秦皇岛市建设局网站关于装配式专家做运动特卖的网站
  • j2ee 建设简单网站设计婚纱网站
  • C++类和对象(中):const 成员函数与取地址运算符重载
  • 数据结构 散列表—— 冲突解决方法
  • 箭头函数和普通函数有什么区别
  • Spring Boot 缓存知识体系大纲
  • 破局政务数字化核心难题:金仓数据库以国产化方案引领电子证照系统升级之路
  • XML:从基础到 Schema 约束的全方位解析
  • 技术引领场景革新|合合信息PRCV论坛聚焦多模态文本智能前沿实践
  • 海南网站建设网络货运平台有哪些
  • 系统架构设计师备考第53天——业务逻辑层设计
  • 科技创新与数字化制造转型在“十五五”规划中的意义
  • 网站开发最新技术wordpress4.7.4密码
  • HarmonyOS方舟编译器与运行时优化
  • HarmonyOS AI能力集成与端侧推理实战
  • 自己做公众号和小说网站推广济南网站建设艮安
  • 阿里云国际站GPU:阿里云GPU的应用场景有哪些?
  • 【工具】Scrcpy|安卓投屏电脑的开源工具Scrcpy的安装及看电视注意事项
  • penCV轻松入门_面向python(第七章 图像平滑处理)
  • html5移动网站开发流程各类设计型网站
  • 使用C#代码在Excel中创建数据透视表
  • 反爬克星还是效率神器?Browser-Use+cpolar重构Web自动化逻辑
  • 《KingbaseES数据库:首个多院区异构多活容灾架构,浙人医创新开新篇》
  • MySQL 的 MyISAM 与 InnoDB 存储引擎的核心区别
  • 【Qt开发】容器类控件(一)-> QGroupBox