uni-app锚点跳转及滚动Tab切换(非scroll-view)

夏俊人
2023-12-01

核心代码(记录当前子项距离最外层的高度)

uni.createSelectorQuery().select(`#${id}`).boundingClientRect(data => { //目标位置节点 类或者 id
					uni.createSelectorQuery().select(".idlePages").boundingClientRect((res) => { //最外层盒子节点类或者 id
						uni.pageScrollTo({
							duration: .3, //过渡时间
							scrollTop: data.top - res.top - that.navBarHeight - 40, //到达距离顶部的top值
						})
					}).exec()
				}).exec();

思路

通过上边的核心代码,在数据加载时,记录每一个需要跳转的子项距最外层的高度,

然后利用uni的onPageScroll,判断当前滚动条的位置,与已经纪录好的子项高度进行比较,判断其在哪一个区间,来改变tab的位置;实现页面上下滚动监听;

再利用watch监听,监听tab的变化,判断其左滑还是右滑,来改变scroll-view距左边的距离,来实现顶部tab栏的左右的鉴定;

页面部分

<template>
	<view class="idlePages">
		<my-fixedTop>
			<view id="myNav">
				<my-nav left-icon="back" :title="title" @click-left="back" shadow="none" :statusBar="true"></my-nav>
				<view class="nav-top-bar" v-if="isShowTopBar">
					<scroll-view scroll-x scroll-with-animation class="nav-top-bar-c" :scroll-left="scrollLeft">
						<view v-for="(item, index) in tabList" :key="index" class="nav-bar-item" :class="[currentTab == index ? 'active' : '']"
						 @tap.stop="swichNav(index)">
							<text class="tab-bar-title">{{ item.category_top_name }}</text>
						</view>
					</scroll-view>
				</view>
			</view>
		</my-fixedTop>
		<view class="idle-container" :style="{paddingTop:navBarHeight + 'px'}">
			<view class="idle-top">
				<image class="idle-bg" :src="imgUrl +'idle/idle-bg.png'" mode=""></image>
				<view class="nav-bar">
					<scroll-view scroll-x scroll-with-animation class="nav-bar-c" :scroll-left="scrollLeft">
						<view v-for="(item, index) in tabList" :key="index" class="nav-bar-item" :class="[currentTab == index ? 'active' : '']"
						 @tap.stop="swichNav(index)">
							<text class="tab-bar-title">{{ item.category_top_name }}</text>
						</view>
					</scroll-view>
				</view>
			</view>
			<view class="idle-container-c" v-if="tabList.length" style="margin-top: -74rpx;">
				<view class="idle-container-con" :id="item.id" v-for="(item,index) in tabList" :key="index">
					<view class="idle-header-c" :class="index==0?'first':''">
						<image :src="imgUrl+'idle/idle-icon-left.png'" mode="" class="idle-icon"></image>
						<view class="idle-title">
							{{item.category_top_name}}
						</view>
						<image :src="imgUrl+'idle/idle-icon-right.png'" mode="" class="idle-icon"></image>
					</view>
					<view class="idle-goods-list">
						<view class="idle-goods-item" v-for="item2,index2 in item.product_list" :key="index2">
							<view class="idle-goods-item-c">
								<view class="goods-item-l">
									<image class="goods-icon" :src="imgUrl+'idle/goods-icon1.png'" mode="" v-if="item2.show_order==1"></image>
									<image class="goods-icon" :src="imgUrl+'idle/goods-icon2.png'" mode="" v-if="item2.show_order==2"></image>
									<image class="goods-icon" :src="imgUrl+'idle/goods-icon3.png'" mode="" v-if="item2.show_order==3"></image>
									<image class="goods-img" :src="item2.img" mode="aspectFill"></image>
								</view>
								<view class="goods-item-r">
									<view class="good-item-title">
										{{item2.template_name}}
									</view>
									<view class="good-item-brand">
										{{item2.brand_name}}
									</view>
									<view class="good-item-price">
										<view class="good-price-l">
											<text class="price-title">行情价:</text>
											<text class="price">¥{{item2.price}}</text>
										</view>
										<view class="good-price-r">
											<text class="ranking-title">较昨日:</text>
											<text class="ranking" :class="item2.diff_price_color=='r'?'r':'g'">{{item2.diff_price}}</text>
										</view>
									</view>
								</view>
							</view>
						</view>
						<view class="load-more-box" v-if="item.loadMore">
							<view class="load-more" @tap.stop="loadMore(item,index)">
								<text>展开更多</text>
								<image :src="imgUrl+'idle/load_more.png'" mode=""></image>
							</view>
						</view>
					</view>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	let that;
	export default {
		data() {
			return {
				title: "测试",
				allPathUrl: this.$allPathUrl,
				pathUrl: this.$pathUrl,
				imgUrl: this.$imgUrl,
				navBarHeight: this.$store.state.navBarHeight, //导航栏高度
				scrollLeft: 0, //tab标题的滚动条位置
				currentTab: 0,
				activeH: 0,
				isShowTopBar: false,
				tabList: [],
				activeTab: {},
				jsonData: {
					size: 6,
					action: 'index',
				},
			}
		},
		watch: {
			currentTab(newName, oldName) {
				this.$nextTick(function() {
					if (newName % 3 == 0 && newName !== 0) {
						if (oldName < newName) {
							this.scrollLeft = this.scrollLeft + 300;
						} else {
							this.scrollLeft = this.scrollLeft - 300;
						}
					}
				})

			}
		},
		onLoad() {
			that = this;
			this.loadInit();
		},
		methods: {
			// 初始化函数
			loadInit() {
				this.$api
					.getInfo(
						'XXXXXX', this.jsonData,
						''
					)
					.then(res => {
						if (res.result == 'ok') {
							this.tabList = res.rec_info;
							this.tabList.forEach((ite, idx) => {
								this.$set(ite, 'loadMore', true);
								// 每个子项设置id名称;
								this.$set(ite, 'id', `idle-header${idx}`);
								// 记录每项的高度;
								this.$nextTick(function() {
									uni.createSelectorQuery().select(`#${ite.id}`).boundingClientRect(data => { //目标位置节点 类或者 id
										uni.createSelectorQuery().select(".idlePages").boundingClientRect((res) => { //最外层盒子节点类或者 id
											that.$set(ite, 'idT', data.top - res.top - that.navBarHeight - 40);
										}).exec()
									}).exec();
								})
							})
						} else {
							this.$utils.uniShowToast(res.info);
						}
					})
			},
			//展开更多
			loadMore(item, index) {
				var loadData = {
					size: 6,
					action: 'view_more',
					category_top_id: item.category_top_id,
					start: item.product_list.length,
				}
				this.$api
					.getInfo(
						'XXXXXX', loadData,
						''
					)
					.then(res => {
						if (res.result == 'ok') {
							if (res.rec_info.length) {

								this.$nextTick(function() {
									this.tabList[index].product_list = this.tabList[index].product_list.concat(res.rec_info);
									// 计算小于当前项的其它项高度;
									setTimeout(function() {
										that.getOtherTop(index);
									}, 1500);

								})
							} else {
								this.$nextTick(function() {
									this.tabList[index].loadMore = false;
								})
								this.$utils.uniShowToast("没有更多了");
							}
						} else {
							this.$utils.uniShowToast(res.info);
						}
					})
			},
			/**
			 * 返回上一页
			 */
			back() {
				this.$utils.goBack();
			},
			// 选择tab下标实现锚点跳转
			swichNav(index) {
				this.currentTab = index;
				let str = `idle-header${index}`;
				this.goTabContainer(str, index);
			},
			// 锚点跳转
			goTabContainer(id, index) {
				uni.createSelectorQuery().select(`#${id}`).boundingClientRect(data => { //目标位置节点 类或者 id
					uni.createSelectorQuery().select(".idlePages").boundingClientRect((res) => { //最外层盒子节点类或者 id
						uni.pageScrollTo({
							duration: .3, //过渡时间
							scrollTop: data.top - res.top - that.navBarHeight - 40, //到达距离顶部的top值
						})
					}).exec()
				}).exec();
			},
			//计算当前元素距页面顶部的高度
			getOtherTop(index) {
				// 循环计算除自身以外其它的子项
				this.tabList.forEach((ite, idx) => {
					if (idx > index) {
						uni.createSelectorQuery().select(`#${this.tabList[idx].id}`).boundingClientRect(data => { //目标位置节点 类或者 id
							uni.createSelectorQuery().select(".idlePages").boundingClientRect((res) => { //最外层盒子节点类或者 id
								that.$nextTick(function() {
									this.tabList[idx].idT = data.top - res.top - that.navBarHeight - 40;
								})
							}).exec()
						}).exec();
					}
				})
			},

		},

		onPageScroll(e) {
			if (e.scrollTop > 350) {
				this.isShowTopBar = true;
			} else {
				this.isShowTopBar = false;
			}
			// 生成高度区间数组
			let arr2 = [];
			this.tabList.forEach((ite, idx) => {
				let arr = [];
				arr.push(ite.idT)
				arr2 = arr2.concat(arr);
			})

			// 控制高度区间
			for (var i = 0; i < arr2.length; i++) {
				if (e.scrollTop < (arr2[i + 1])) {
					this.currentTab = i;
					break;
				}
			}
		},
	}
</script>

<style lang="scss">
	page {
		background-color: #D47B57;
	}

	.idlePages {
		.nav-top-bar {
			width: 100%;
			height: 80rpx;
			background-color: #CE6D45;

			.nav-top-bar-c {
				width: 650rpx;
				height: 80rpx;
				margin: 0 auto;
				white-space: nowrap;

				.nav-bar-item {
					height: 80rpx;
					margin-right: 140upx;
					color: #EBB69E;
					font-size: 28rpx;
					display: inline-block;
					font-weight: 400;
					line-height: 80rpx;
				}

				.nav-bar-item:last-child {
					margin-right: 0rpx;
				}

				.nav-bar-item.active {
					color: #FFFFFF;
					font-weight: 500;
				}
			}
		}

		.idle-container {
			padding-bottom: env(safe-area-inset-bottom);

			.idle-top {
				width: 750rpx;
				height: 800rpx;
				position: relative;

				.idle-bg {
					width: 750rpx;
					height: 800rpx;
					position: absolute;
					top: 0;
					left: 0;
					z-index: -1;
				}

				.nav-bar {
					width: 750rpx;
					height: 80rpx;
					position: absolute;
					bottom: 80rpx;
					left: 0rpx;
					z-index: 0;

					.nav-bar-c {
						width: 650rpx;
						height: 80rpx;
						margin: 0 auto;
						white-space: nowrap;

						.nav-bar-item {
							height: 80rpx;
							margin-right: 140upx;
							color: #EBB69E;
							font-size: 28rpx;
							display: inline-block;
							font-weight: 400;
							line-height: 80rpx;
						}

						.nav-bar-item:last-child {
							margin-right: 0rpx;
						}

						.nav-bar-item.active {
							color: #FFFFFF;
							font-weight: 500;
						}
					}
				}

				.idle-header-top {
					position: absolute;
					left: 0;
					bottom: 0;
				}
			}

			.idle-header {
				width: 750rpx;
				height: 80rpx;
			}

			.idle-header-c {
				width: 690rpx;
				height: 80rpx;
				margin: 0 auto;
				margin-top: 30rpx;
				display: flex;
				align-items: center;
				justify-content: center;

				.idle-icon {
					width: 36rpx;
					height: 30rpx;
				}

				.idle-title {
					font-size: 40rpx;
					color: #fff;
					font-weight: 500;
					line-height: 80rpx;
					margin: 0 20rpx;
				}
			}

			.idle-header-c.first {
				margin-top: 0rpx;
			}

			.idle-container-c {
				width: 690rpx;
				margin: 0 auto;
				padding-bottom: 20rpx;

				.idle-goods-list {
					width: 100%;

					.idle-goods-item {
						width: 100%;
						height: 260rpx;
						margin-bottom: 20rpx;
						background-color: #fff;
						border: 4rpx;
						position: relative;

						.idle-goods-item-c {
							width: 650rpx;
							height: 220rpx;
							position: absolute;
							top: 0;
							left: 0;
							right: 0;
							bottom: 0;
							margin: auto;
							display: flex;
							align-items: center;
							justify-content: space-between;

							.goods-item-l {
								width: 240rpx;
								height: 220rpx;
								position: relative;

								.goods-icon {
									width: 44rpx;
									height: 60rpx;
									position: absolute;
									top: -20rpx;
									left: 0;
								}

								.goods-img {
									width: 100%;
									height: 100%;
									// background-color: red;
								}
							}

							.goods-item-r {
								width: 389rpx;
								height: 220rpx;

								.good-item-title {
									width: 100%;
									height: 80rpx;
									font-size: 28rpx;
									font-weight: 400;
									color: #333;
									line-height: 42rpx;
									text-overflow: -o-ellipsis-lastline;
									overflow: hidden;
									text-overflow: ellipsis;
									display: -webkit-box;
									-webkit-line-clamp: 2;
									line-clamp: 2;
									-webkit-box-orient: vertical;
								}

								.good-item-brand {
									font-size: 25rpx;
									color: #333;
									line-height: 25rpx;
									margin-top: 20rpx;
								}

								.good-item-hot {
									margin-top: 20rpx;
									width: 202rpx;
									height: 32rpx;
									background-color: #FFE1E0;
									border-radius: 20rpx;
									font-size: 22rpx;
									font-weight: 400;
									line-height: 32rpx;
									color: #D33032;
									position: relative;

									.hot-icon {
										width: 32rpx;
										height: 32rpx;
										position: absolute;
										top: 0;
										left: 0;
									}

									.hot-title {
										margin-left: 40rpx;
									}
								}

								.good-item-price {
									width: 100%;
									height: 40rpx;
									margin-top: 55rpx;
									display: flex;
									align-items: center;
									justify-content: space-between;
									font-weight: 400;

									.price-title {
										font-size: 24rpx;
										color: #666;
										line-height: 24rpx;
									}

									.price {
										line-height: 32rpx;
										color: #F1982C;
									}

									.ranking-title {
										font-size: 22rpx;
										color: #999;
										line-height: 22rpx;
									}

									.ranking {
										font-size: 22rpx;
										line-height: 22rpx;
									}

									.r {
										color: #D63C3E;
									}

									.g {
										color: #71BB31;
									}
								}
							}
						}
					}

					.load-more-box {
						width: 100%;
						height: 55rpx;
						margin-top: 40rpx;

						.load-more {
							width: 208rpx;
							height: 48rpx;
							margin: 0 auto;
							border: 1px solid #fff;
							border-radius: 32rpx;
							display: flex;
							align-items: center;
							justify-content: center;

							text {
								line-height: 48rpx;
								font-size: 24rpx;
								color: #fff;
							}

							image {
								width: 14rpx;
								height: 8rpx;
								margin-left: 8rpx;
							}
						}
					}
				}
			}
		}

	}
</style>

 类似资料: