vue项目实现Tab页面触底上拉切换下个Tab

如题,实现Tab页面触底上拉切换下一Tab的功能,关键流程:下滑触底——显示“上拉切换下一页”——继续上拉超过一定距离(如100px)——切换下一页,重置相关参数。
关键代码:
一. 处理滚动的业务逻辑封装为goodsListScroll.js,代码如下:
//触底上拉切换下一页相关代码 export default { props: { last_tab: false, //是否是最后一个tab }, data() { return { startY: 0, maxScroll: 0, //最大可滚动距离 showHint: false, //底部显示切换下一页提示条 touchStart: false, //触摸开始置为true,触发Tab切换后重置为false,终止handleTouchMove中的逻辑 isScrolling: false, //显示“切换下一Tab”后触摸开始标记为true、切换Tab后重置为false }; }, beforeDestroy() { // 移除事件监听 this.$refs.container.removeEventListener('touchstart', this.handleTouchStart); this.$refs.container.removeEventListener('touchmove', this.handleTouchMove); }, methods: { //重新计算最大滚动值(因为内容可能变化),获取商品列表后调用 calculateMaxScroll() { if (this.$refs.container) { const element = this.$refs.container; this.maxScroll = element.scrollHeight - element.clientHeight; //不可滚动页面直接显示 if (this.maxScroll === 0 && (this.active < this.subCateList.length - 1)) { this.showHint = true } } }, //容器滚动监听事件 handleScroll(event) { const element = event.target; this.scrollTop = element.scrollTop; // 计算滚动百分比 this.scrollPercentage = this.maxScroll > 0 ? Math.min(100, Math.round((this.scrollTop / this.maxScroll) * 100)) : 0; // 检查是否滚动到底部 if (element.scrollHeight == element.clientHeight) return; //初始化的时候高度会相等 const isAtBottom = element.scrollHeight - element.scrollTop <= element.clientHeight + 10; // 显示或隐藏提示 if (this.active < this.subCateList.length - 1) this.showHint = isAtBottom; }, /** * @param {Object} e * 触摸开始标记,必须在这里设置,切换tab后置为false,终止handleTouchMove */ handleTouchStart(e) { this.touchStart = true }, /** * 下滑触底上拉切换tab * 1. touchStart:触摸开始标志,切换tab后为false,终止代码执行 * 2. isScrolling:页面上拉触底后置为true,同时记录当前位置作为滑动开始位置 * 3. startY:计算触底上拉的初始位置,与实际触摸开始无关 */ handleTouchMove(e) { //1. 滑动过程中已经触发过Tab切换的就不再执行 if (!this.touchStart) return; //2. 已触底且isScrolling为false时获取滑动的位置,并将isScrolling置为true if (this.showHint && !this.isScrolling) { this.isScrolling = true //3. 设置计算滑动距离的初始位置 this.startY = e.touches[0].clientY; } //滑动过程中实时获取当前位置,并计算滑动距离 const currentY = e.touches[0].clientY; const deltaY = this.startY - currentY; // 正值表示向上滑动 // 只有向上滑动才处理 if (deltaY > 0) { // 当向上滑动距离超过100px时触发切换 if (deltaY > 100) { this.switchToNextTab(); } } }, //切换至下一个Tab switchToNextTab() { // 移除事件监听 this.$refs.container.removeEventListener('touchstart', this.handleTouchStart); this.$refs.container.removeEventListener('touchmove', this.handleTouchMove); if (this.active < this.subCateList.length - 1) { this.active = this.active + 1 //重置其他参数 this.touchStart = false this.showHint = false; this.isScrolling = false; this.startY = 0; this.maxScroll = 0 } } } };
以上为完整代码,关键点有这些:
1. calculateMaxScroll():计算容器最大滚动值,在获取到商品列表数据后调用,不可滚动页面直接显示“上拉切换下一页”提示条;
2. handleScroll():商品列表容器滚动监听事件,针对可滚动页面在滑动到页面底部时,显示“上拉切换下一页”提示条;
3. handleTouchStart():滑动开始事件,将滑动开始标志touchStart置为true;
4. handleTouchMove():滑动过程监听,触底上拉超过一定距离,触发切换下一Tab的逻辑switchToNextTab();
5. switchToNextTab():切换下一Tab逻辑,移除滚动监听,所有相关参数重置。
代码中已经加了详细的注释,可以结合注释来看。
二. 页面相关代码:
<template> <div id="container" class="h100" ref="container" @scroll="handleScroll"> <!--tab栏--> <van-tabs id="tabs" class="p_fixed top left w100" v-model="active" :title-active-color="redColor" :background="primaryColor" :color="primaryColor" title-inactive-color="#a8a8a8" swipe-threshold="4" sticky> <van-tab v-for="(item,index) in cateList" :title="item.title"> </van-tab> </van-tabs> <!--商品列表栏--> <div class="goods_list w100 flex space_between" v-show="goodsList"> <!--商品列表展示代码--> </div> <!--触达切换tab--> <div v-if="!last_tab" class="switch-hint p_fixed c_f flex center f12" :class="{ show: showHint,hide:!showHint&&startY==0 }"> 上拉切换下一页 </div> </div> </template> <script> import { Tabs, Tab } from 'vant'; import goodsListScroll from "@/assets/js/mixin/goodsListScroll.js" export default { name: "CateGoods", mixins: [goodsListScroll], components: { Nodata, [Tab.name]: Tab, [Tabs.name]: Tabs }, data() { return { active: 0, cateList: '', //子分类列表 goodsList: '', //瀑布流商品列表 } }, watch: { //监听tab选择索引,获取对应的商品列表 active(newValue, oldValue) { if (newValue > -1) { this.goodsList = '' let sub_rack_id = this.subCateList[this.active].sub_rack_id //获取商品列表 this.getGoodsList(sub_rack_id, this.rack_id) } } }, methods: { /** * @param {Object} rack_id * 获取商品列表 */ getGoodsList(rack_id, rack_parent_id) { //调用接口获取商品列表数据,这里代码省略 //获取商品列表后监听页面滚动 if (!this.last_tab) { // 确保DOM更新后计算最大滚动值 this.$nextTick(() => { this.scrollTopFn() // 添加触摸事件监听 this.$refs.container.addEventListener('touchstart', this.handleTouchStart, { passive: false }); this.$refs.container.addEventListener('touchmove', this.handleTouchMove, { passive: false }); //计算页面可以滚动距离 this.calculateMaxScroll(); }); } } else { this.goodsList = [] } }); } </script> <style lang="scss" scoped> #container { overflow-y: scroll; } /*滚动触底上拉切换下一个Tab相关样式*/ .switch-hint { bottom: 30px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.7); padding: 12px 30px; border-radius: 25px; opacity: 0; transition: opacity 0.5s, transform 0.5s; pointer-events: none; z-index: 100; } .switch-hint.show { opacity: 1; transform: translateX(-50%) translateY(-10px); } .switch-hint.hide { transition: none; } </style>
如上,页面+goodsListScroll即可实现Tab页面的触底上拉切换下一Tab的功能。
其中添加“上拉切换下一页”是为了优化用户体验,如果没有提示条,直接切换回显的有些突兀。
后记:
比较关键的点就是触底上拉切换Tab应该以触底上拉一定距离触发切换下一Tab的逻辑之后就应该结束,跟滑动是否结束无关。否则将会出现手指不松开一直上拉,可能会一直触发切换Tab,这个点要注意。
个人原创博客,转载请注明来源地址:https://www.cnblogs.com/xyyt
浙公网安备 33010602011771号