uniapp使用scroll-into-view实现锚点定位和滚动监听功能【楼层效果 / 侧边导航联动效果】

news2024/11/27 23:45:02

大佬网址:

https://blog.csdn.net/weixin_47136265/article/details/132303570

效果

请添加图片描述

代码

<template>
	<!-- 这里面有2个bug,已经解决,需要知道的地方
	 1.methods里的scrollEvt(e)方法里面的  this.tabIndex = index != -1 ?  index : 0;
	 2. 样式里面的.main 的height: calc(100vh - 300px);这个样式防止,分类滑动的时候,整体滑倒页面顶部bug,
	 设置固定高度,就不会整体滑动到页面顶部了
	 -->
	<view class="max">
		<view class="box">
			123
		</view>
		<view class="main">
			<scroll-view scroll-y="true" class="left-content">
				<view class="left-item" :class="{ 'activeItem': tabIndex == index }" v-for="(item,index) in leftData"
					:key="item.id" @click="clickLeftItem(index)">{{item.title}}</view>
			</scroll-view>
			<scroll-view scroll-y="true" class="right-content" :scroll-into-view="scrollId" scroll-with-animation
				@scroll="scrollEvt">
				<view class="right-item" v-for="(item,index) in rightData" :key="item.id" :id="'item'+index">
					<view class="title">
						{{item.title}}
					</view>
					<view class="content">
						<view class="content-item" v-for="itm in item.children" :key="itm.id">
							{{itm.text}}
						</view>
					</view>
				</view>
			</scroll-view>
		</view>
	</view>
</template>
<script>
	export default {
		data() {
			return {
				tabIndex: 0,
				scrollId: '',
				distanceList: [],
				timer: null,
				isLeftClick: false,
				leftData: [{
						title: '第一个',
						id: 10001
					},
					{
						title: '第二个',
						id: 10002
					},
					{
						title: '第三个',
						id: 10003
					},
					{
						title: '第四个',
						id: 10004
					},
					{
						title: '第五个',
						id: 10005
					},
					{
						title: '第六个',
						id: 10006
					}
				],
				rightData: [{
						id: '20001',
						title: '第一部分标题',
						children: [{
								id: '30001',
								text: '第一个子项'
							},
							{
								id: '30002',
								text: '第二个子项'
							},
							{
								id: '30003',
								text: '第三个子项'
							},
							{
								id: '30004',
								text: '第四个子项'
							}
						]
					},
					{
						id: '20002',
						title: '第二部分标题',
						children: [{
								id: '30005',
								text: '第一个子项'
							},
							{
								id: '30006',
								text: '第二个子项'
							},
							{
								id: '30007',
								text: '第三个子项'
							}
						]
					},
					{
						id: '20003',
						title: '第三部分标题',
						children: [{
								id: '30008',
								text: '第一个子项'
							},
							{
								id: '30009',
								text: '第二个子项'
							},
							{
								id: '30010',
								text: '第三个子项'
							},
							{
								id: '30011',
								text: '第四个子项'
							}
						]
					},
					{
						id: '20004',
						title: '第四部分标题',
						children: [{
								id: '30012',
								text: '第一个子项'
							},
							{
								id: '30013',
								text: '第二个子项'
							}
						]
					},
					{
						id: '20005',
						title: '第五部分标题',
						children: [{
								id: '300014',
								text: '第一个子项'
							},
							{
								id: '300015',
								text: '第二个子项'
							}
						]
					},
					{
						id: '20006',
						title: '第六部分标题',
						children: [{
								id: '300016',
								text: '第一个子项'
							},
							{
								id: '300017',
								text: '第二个子项'
							},
							{
								id: '300018',
								text: '第三个子项'
							},
							{
								id: '300019',
								text: '第四个子项'
							}
						]
					}
				]
			}
		},
		mounted() {
			setTimeout(() => {
				this.getDistanceToTop();
			}, 500)
		},

		methods: {
			clickLeftItem(index) {
				this.isLeftClick = true
				this.tabIndex = index
				this.scrollId = 'item' + index
			},
			getDistanceToTop() { //获取右侧各部分距离顶部的距离
				let that = this
				let selectorQuery = uni.createSelectorQuery().in(this);

				selectorQuery.selectAll('.right-item').boundingClientRect(function(rects) {
					rects.forEach(function(rect) {
						that.distanceList.push(rect.top)
					})
					console.log('that.distanceList', that.distanceList);
				}).exec()
			},

			// 元素滚动到顶部时,对应的左侧导航栏变为选中状态
			scrollEvt(e) {
				// 点击左侧导航栏引起的滚动不做判断
				if (this.isLeftClick) {
					this.isLeftClick = false
					return
				}
				// 防抖
				if (this.timer) {
					clearTimeout(this.timer)
				}
				this.timer = setTimeout(() => {
					let scrollTop = e.detail.scrollTop //滚动的高度
					// 找到位于顶部元素的索引,距离大于滚动高度的第一个元素的上一个元素就是此时位于顶部的元素
					let index = this.distanceList.findIndex(it => {
						// 滚动条的位置大于元素距离顶部位置的距离时,说明元素已经滑过了顶部
						return (it > scrollTop)
					}) - 1
					if (index == this.tabIndex) return
					// this.tabIndex = index;		// 这里是个	bug

					this.tabIndex = index != -1 ? index : 0; //  修改后可以了正确了,找人花了我50大洋,五分钟解决,太厉害了!!!
				}, 200)
			}
		}
	}
</script>
<style lang="less" scoped>
	.max {
		display: flex;
		flex-direction: column;
	}

	.box {
		width: 100%;
		height: 200rpx;
		border: 1rpx solid red;
	}

	.main {
		display: flex;
		justify-content: space-between;
		width: 100vw;
		// 这里控制楼层高度,防止全部滑动到页面顶部,这里设置高度,这样设置!!!
		height: calc(100vh - 300px);
		// 或者用这个var(--status-bar-height) 是app的上面状态栏高度
		//height: calc(100vh - var(--status-bar-height) - 300rpx);//重点!!!
		margin-top: 100rpx;
		border: 1rpx solid red;
		
	}

	.left-content {
		width: 250rpx;
		height: 100%;
		background-color: #E7E7E7;
	}

	.left-item {
		width: 100%;
		height: 100rpx;
		text-align: center;
		line-height: 100rpx;
	}

	.activeItem {
		background-color: #fff;
		color: skyblue;
	}

	.right-content {
		flex: 1;
		height: 100%;
		background-color: #f4f4f4;
	}

	.content-item {
		width: 100%;
		height: 200rpx;
		background-color: aqua;
		margin-top: 20rpx;
		overflow:auto;//重点
		height: calc(100vh - var(--status-bar-height) - 300rpx);//重点!!!
	}

	.title {
		padding: 15px 0 0 10px;
	}

	// 去掉右部分有滚动条
	/deep/::-webkit-scrollbar {
		display: none;
		width: 0;
		height: 0;
	}
</style>

大佬

效果

请添加图片描述

代码

<template>
	<view class="bodys">
		<view class="scroll_box" id="scroll_box">
			<scroll-view :style="{ height: scrollHeight + 'px' }" scroll-y='true' class="left_box"
				:scroll-into-view="leftIntoView">
				<view class="left_item" v-for="(item,i) in leftArray" :key='i' @click="onLeft" :data-index="i"
					:id="'left-'+i" :class="{select:i == leftIndex}">
					{{item}}
				</view>
			</scroll-view>
			<scroll-view :style="{ height: scrollHeight + 'px' }" @scroll="mainScroll" :scroll-into-view="scrollInto"
				scroll-y='true' class="right_box" scroll-with-animation="true">
				<slot></slot>
				<view class="right_item" v-for="(item,i) in rightArray" :key='i' :id="'item-'+i">
					<view class="rigth_title">
						{{item.title}}
					</view>
					<view class="lis" v-for="(items,j) in item.list" :key='j'>
						{{items}}
					</view>
				</view>
				<view class="fill-last" :style="{ height: fillHeight + 'px' }"></view>
			</scroll-view>
		</view>
	</view>
</template>

<script>
	export default {
		name: "side-navigation",
		data() {
			return {
				leftArray: [],
				rightArray: [],
				scrollHeight: 400,
				scrollInto: "",
				leftIndex: 0,
				topArr: [],
				scrollTopSize: 0,
				fillHeight: 0, // 填充高度,用于最后一项低于滚动区域时使用
			}
		},
		computed: {
			/* 计算左侧滚动位置定位 */
			leftIntoView() {
				return `left-${this.leftIndex > 3 ? this.leftIndex - 3 : 0}`;
			}
		},
		mounted() {
			/* 等待DOM挂载完成 */
			this.$nextTick(() => {
				/* 在非H5平台,nextTick回调后有概率获取到错误的元素高度,则添加200ms的延迟来减少BUG的产生 */
				setTimeout(() => {
					/* 等待滚动区域初始化完成 */
					this.initScrollView().then(() => {
						/* 获取列表数据,你的代码从此处开始 */
						this.getListData();
					});
				}, 200);
			});
		},
		methods: {
			/* 初始化滚动区域 */
			initScrollView() {
				return new Promise((resolve, reject) => {
					let view = uni.createSelectorQuery().select('#scroll_box');
					view.boundingClientRect(res => {
						console.log(res);
						this.scrollTopSize = res.top;
						this.scrollHeight = res.height;
						this.$nextTick(() => {
							resolve();
						});
					}).exec();
				});
			},
			// 获取数据
			getListData() {
				new Promise((resolve, reject) => {
					uni.showLoading();
					setTimeout(() => {
						let [left, main] = [
							[],
							[]
						];

						for (let i = 0; i < 25; i++) {
							left.push(`${i + 1}类商品`);

							let list = [];
							let r = Math.floor(Math.random() * 10);
							r = r < 1 ? 3 : r;
							for (let j = 0; j < r; j++) {
								list.push(j);
							}
							main.push({
								title: `${i + 1}类商品标题`,
								list
							});
						}

						// 将请求接口返回的数据传递给 Promise 对象的 then 函数。
						resolve({
							left,
							main
						});
					}, 1000);
				}).then(res => {
					uni.hideLoading();
					this.leftArray = res.left;
					this.rightArray = res.main;
					// DOM 挂载后 再调用 getElementTop 获取高度的方法。
					this.$nextTick(() => {
						this.getElementTop();
					});
				});
			},
			// 获取元素顶部信息
			getElementTop() {
				new Promise((resolve, reject) => {
					let view = uni.createSelectorQuery().selectAll('.right_item');
					view.boundingClientRect(data => {
						resolve(data);
					}).exec();
				}).then(res => {
					console.log(res);
					let topArr = res.map(item => {
						return item.top - this.scrollTopSize; /* 减去滚动容器距离顶部的距离 */
					});
					this.topArr = topArr;

					/* 获取最后一项的高度,设置填充高度。判断和填充时做了 +-20 的操作,是为了滚动时更好的定位 */
					let last = res[res.length - 1].height;
					if (last - 20 < this.scrollHeight) {
						this.fillHeight = this.scrollHeight - last + 20;
					}
				});
			},
			// 点击左侧导航
			onLeft(e) {
				const index = e.currentTarget.dataset.index;
				// this.leftIndex = index
				this.scrollInto = `item-${index}`
			},
			// 右侧滑动
			mainScroll(e) {
				let top = e.detail.scrollTop;
				let index = 0;
				/* 查找当前滚动距离 */
				for (let i = this.topArr.length - 1; i >= 0; i--) {
					/* 在部分安卓设备上,因手机逻辑分辨率与rpx单位计算不是整数,滚动距离与有误差,增加2px来完善该问题 */
					if (top + 2 >= this.topArr[i]) {
						index = i;
						break;
					}
				}
				this.leftIndex = index < 0 ? 0 : index;
			},
		},

	}
</script>

<style>
	page,.bodys {
		height: 100%;
	}

	.scroll_box {
		display: flex;
		height: 100%;
	}

	.left_box {
		width: 30%;
	}

	.left_item {
		height: 80rpx;
	}

	.lis {
		height: 200rpx;
		border-radius: 10rpx;
		background: #808080;
		color: #FFFFFF;
		text-align: center;
		line-height: 200rpx;
		margin-bottom: 10rpx;
	}

	.select {
		background-color: #4CD964;
	}
</style>

Vant-UI官网

网址1

网址2

推荐uniapp插件市场

uniapp插件市场搜索商品分类!!!
好用,兼容性各个端,好用,我就用的这个插件!!!

地址

https://ext.dcloud.net.cn/plugin?id=13148

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1052688.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python -m pip install --upgrade pip失败

显示这样的报错&#xff1a; You are using pip version 9.0.1, however version 23.2.1 is available. You should consider upgrading via the python -m pip install --upgrade pip command. 换源安装 python -m pip install --upgrade pip -i https://pypi.douban.com/s…

2023-9-30 JZ34 二叉树中和为某一值的路径

题目链接&#xff1a;二叉树中和为某一值的路径 import java.util.*;/** public class TreeNode {* int val 0;* TreeNode left null;* TreeNode right null;* public TreeNode(int val) {* this.val val;* }* }*/public class Solution {/*** 代码中的类名、…

1.物联网射频识别,RFID概念、组成、中间件、标准,全球物品编码——EPC码

1.RFID概念 RFID是Radio Frequency Identification的缩写&#xff0c;又称无线射频识别&#xff0c;是一种通信技术&#xff0c;可通过无线电讯号识别特定目标并读写相关数据&#xff0c;而无需与被识别物体建立机械或光学接触。 RFID&#xff08;Radio Frequency Identificati…

MyBatis的一级缓存和二级缓存:原理和作用

MyBatis的一级缓存和二级缓存&#xff1a;原理和作用 引言 在数据库访问中&#xff0c;缓存是一种重要的性能优化手段&#xff0c;它可以减少数据库查询的次数&#xff0c;加快数据访问速度。MyBatis作为一款流行的Java持久层框架&#xff0c;提供了一级缓存和二级缓存来帮助…

机器学习之广义增量规则(Generalized Delta Rule)

文章目录 广义增量规则的公式s型函数的增量规则 广义增量规则的公式 对于单层神经网络的增量规则&#xff0c;已经过时啦&#xff0c;现在存在一种更广义的增量规则形式。对于任意激活函数&#xff0c;增量规则表示如下式它与前一节的delta规则相同&#xff0c;只是ei被替换为…

案例突破——再探策略模式

再探设计模式 一、背景介绍二、 思路方案三、过程1. 策略模式基本概念2. 策略模式类图3. 策略模式基本代码策略类抽象策略类Context类客户端 4. 策略模式还可以进行优化的地方5. 对策略模式的优化&#xff08;配置文件反射&#xff09; 四、总结五、升华 一、背景介绍 在做项目…

ccf_csp第一题汇总

ccf_csp第一题汇总 printf()输出格式大全&#xff08;附 - 示例代码&#xff09;现值计算AcWing 4699. 如此编码AcWing 4509. 归一化处理(小数位数根号函数)AcWing 4454. 未初始化警告AcWing 4280. 序列查询AcWing 4006. 数组推导(小陷阱)AcWing 3292. 称检测点查询AcWing 3287…

git你学“废”了吗?——git本地仓库的创建

git你学“废”了吗&#xff1f;——git本地仓库的创建&#x1f60e; 前言&#x1f64c;初识gitgit 本地仓库的创建1、基于centos7环境下 git的下载2、设置自己的用户名和邮箱 查看.git中的结构区分清楚版本库和工作区 查看git中的相关内容查看仓库的状态 总结撒花&#x1f49e;…

给奶牛做直播之一

一、前言 前几天看了薇亚写的一本书《人生是用来改变的》&#xff0c;和同事聊的时候同事问我这本书讲了什么&#xff0c;我回想了一下说主要是讲&#xff1a;我很辛苦&#xff0c;我很上进&#xff0c;我很牛逼&#xff0c;我很善良&#xff01;话说成功人士放个屁都是香的&am…

[题]修剪草坪 #单调队列优化

题目 洛谷上的题目 Acwing上的题目 根据y总的一波分析&#xff0c;我们得出……公式就是一切…… 所以&#xff0c;我要学会推公式…… 推公式…… 公式…… #include<bits/stdc.h> using namespace std; typedef long long ll; const int N 1e5 10; int n, m; ll s[N…

知识库系统都有哪些?知识库系统功能和介绍

知识库的建立是企业数字化转型的重要一步&#xff0c;能够为企业的可持续发展奠定坚实的基础。使用HelpLook创建的知识库知识库系统已经成为当前很多人的选择。 HelpLook可以实现并制作企业帮助中心设计、产品说明书、常见问题手册、在线知识库、企业文档制作。 点击注册使用&…

pip version 更新

最近报了一个错&#xff1a; 解决办法&#xff1a; 在cmd输入“conda install pip” conda install pip 完了之后再输入&#xff1a; python -m pip install --upgrade pip ok.

面试题六:Promise的使用,一文详细讲解

含义 Promise是异步编程的一种解决方案&#xff0c;比传统的解决方案&#xff08;回调函数和事件&#xff09;更合理更强大。 所谓Promise&#xff0c;简单说就是一个容器&#xff0c;里面保存着某个未来才会结束的事件 (通常是一个异步操作)的结果。从语法上说&#xff0c;P…

WPF 01

xaml是声明性语言 每见到一个标签&#xff0c;就意味着xaml为我们声明一个标签对应的对象。 在XAML中为对象属性赋值 1. AttributeValue形式 <Grid><Rectangle Width"100" Height"80" Stroke"Black" Fill"Blue" RadiusX&q…

Mybatis 二级缓存(使用Redis作为二级缓存)

上一篇我们介绍了mybatis中二级缓存的使用&#xff0c;本篇我们在此基础上介绍Mybatis中如何使用Redis作为二级缓存。 如果您对mybatis中二级缓存的使用不太了解&#xff0c;建议您先进行了解后再阅读本篇&#xff0c;可以参考&#xff1a; Mybatis 二级缓存https://blog.csd…

oracle分组合并数值带顺序

比如&#xff1a;有如下一张设备电子围栏位置坐标的表&#xff08;tb_equ_point&#xff09;。 equ_name:设备电子围栏名称 point_id:点位坐标id point_x:点位x坐标 point_y:点位y坐标。 附数据&#xff1a; INSERT INTO "tb_equ_point" ("EQU_NAME",…

数据结构题型11-顺序队列

#include <iostream> //引入头文件 using namespace std;typedef int Elemtype;#define Maxsize 5 #define ERROR 0 #define OK 1typedef struct {Elemtype data[Maxsize];int front, rear; }SqQueue;void InitQueue(SqQueue& Q) //初始化队列 {Q.rear Q.front…

使用代理IP进行安全高效的竞争情报收集,为企业赢得竞争优势

在激烈的市场竞争中&#xff0c;知己知彼方能百战百胜。竞争对手的信息对于企业来说至关重要&#xff0c;它提供了洞察竞争环境和市场的窗口。在这个信息时代&#xff0c;代理IP是一种实用的工具&#xff0c;可以帮助企业收集竞争对手的产品信息和营销活动数据&#xff0c;为企…

lv5 嵌入式开发-11 消息队列

掌握&#xff1a;消息队列机制、打开/创建消息队列、发送消息、接收消息 1 消息队列 消息队列是System V IPC对象的一种 消息队列由消息队列ID来唯一标识 消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等 消息队列可以按照类型来发送/接收消息 消…

Win10自带输入法怎么删除-Win10卸载微软输入法的方法

Win10自带输入法怎么删除&#xff1f;Win10系统自带输入法就是微软输入法&#xff0c;这个输入法满足了很多用户的输入需求。但是&#xff0c;有些用户想要使用其它的输入法&#xff0c;这时候就想删除掉微软输入法。下面小编给大家介绍最简单方便的卸载方法吧。 Win10卸载微软…