vue实现导航里面锚点定位和滚动监听功能

news2024/9/24 13:21:46

需求

我们在开发过程中有时候会遇到左侧导航菜单栏数据需要监听和右侧顶部导航菜单联动效果。这里我们通常使用锚点定位和滚动监听方法实现。这里我们使用两种方案解决,第一是常规的出来方法,第二是通过uniapp里面的scroll-view进行处理

具体实现方案如下
一、vue里面处理,通过监听滚动事件和定义元素

1- 代码如下

<template>
  <div>
    <div class="outBox">
      <div class="first">
        <div :class="['level', {'select': i == first.select}]" style="background-color: #666;" v-for="(v, i) in first.list">{{ v }}</div>
      </div>
      <div class="second" id="select">
        <div :class="['level', {'select': i == second.select}]" style="background-color: #666;" v-for="(v, i) in second.list">{{ v }}</div>
      </div>
      <div class="content" id="content">
        <div v-if="status" class="box box1" style="height: 100px;">二级类目1 商品内容</div>
        <div v-if="status" class="box box2" style="height: 344px;">二级类目2 商品内容</div>
        <div v-if="status" class="box box3" style="height: 470px;">二级类目3 商品内容</div>
        <div v-if="status" class="box box4" style="height: 230px;">二级类目4 商品内容</div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      first: {
        select: 0,
        list: ['一级类目1', '一级类目2', '一级类目3', '一级类目4',  '一级类目5']
      },
      second: {
        select: 0,
        list: ['二级类目1', '二级类目2', '二级类目3', '二级类目4']
      },
      list: [],
      timer: null,
      status: true
    }
  },
  methods: {
    scroll() {
      let scrollTop = document.getElementById('content').scrollTop
      let i = this.list.findIndex(v => v >= scrollTop)
      this.second.select = i
      if (scrollTop >= this.list[this.list.length - 1] - 20) {
        this.timer && clearTimeout(this.timer)
        this.timer = setTimeout(() => {
          this.second.select = 0
          this.first.select += 1
          document.getElementById('content').removeEventListener('scroll', this.scroll)
          document.getElementById('content').scrollTo(0, 10)
          document.getElementById('content').addEventListener('scroll', this.scroll)
          // 判断如果左侧导航菜单数据没有的情况默认选中定义导航菜单
          if (this.first.select >= this.first.list.length) {
            this.first.select = 0
          }
        }, 300)
       
      } else if (scrollTop == 0) {
        this.timer && clearTimeout(this.timer)
        this.timer = setTimeout(() => {
          this.second.select = 0
          if (this.second.select > 0) {
            this.first.select -= 1
          } else {
            // 滚动到顶部刷新数据
            this.first.select = 0
          }
          document.getElementById('content').removeEventListener('scroll', this.scroll)
          document.getElementById('content').scrollTo(0, 10)
          document.getElementById('content').addEventListener('scroll', this.scroll)
        }, 300)
      }
    }
  },
  mounted() {
    let o = document.getElementsByClassName('box')
    for ( let i = 0; i < o.length; i++) {
      // 监测右侧导航菜单距离顶部的距离60
      this.list[i] = i > 0 ? o[i-1].scrollHeight + this.list[i - 1] + (60 * (i > 1 ? 1 : 0)) : 10
    }
    let scrollHeight = document.getElementById('content').scrollHeight
    let offsetHeight = document.getElementById('content').offsetHeight
    if (scrollHeight - offsetHeight < this.list[this.list.length - 1]) {
      this.list[this.list.length - 1] = scrollHeight - offsetHeight
    }
    document.getElementById('content').addEventListener('scroll', this.scroll)
  }
}
</script>
<style>
.outBox{
    width: 400px;
    height: 300px;
    margin: 0;
    padding: 0;
    background-color: #eee;
    position: relative;
  }
  .first{
    position: absolute;
    left: 0;
    top: 0;
    width: 100px;
    height: 100%;
    overflow: auto;
    background-color: rgb(102, 102, 102);
  }
  .first .level{
    height: 30px;
    font-size: 14px;
    text-align: center;
    line-height: 30px;
  }
  .second{
    position: absolute;
    left: 100px;
    top: 0;
    width: calc(100% - 100px);
    height: 60px;
    overflow: auto;
    background-color: #06c;
    display: flex;
    align-items: center;
    justify-content: space-around;
  }
  .second .level{
    height: 30px;
    font-size: 14px;
    text-align: center;
    line-height: 30px;
  }
  .content{
    position: absolute;
    left: 100px;
    top: 60px;
    width: calc(100% - 100px);
    height: calc(100% - 60px);
    min-height: 240px;
    overflow: auto;
    box-sizing: border-box;
  }
  .box{
    background-color: aquamarine;
    margin-bottom: 20px;
  }
  .select{
    color: #f56c6c;
  }
</style>

2- 实现结果如下:

在这里插入图片描述
在这里插入图片描述

二、uni-app使用scroll-view锚点定位和滚动监听功能

1- 代码如下
html

<template>
	<view class="classicsBox">
		<scroll-view class="classics-left" scroll-y="true" scroll-with-animation :scroll-into-view="clickId">
			<view v-for="(item,index) in contentData" :key="item.id" :id="item.id" class="classics-left-item" :class="picked==index?'checkedStyle':''" @click="selectActiveEvt(item)">
				{{item.title}}
			</view>
		</scroll-view>
		<!-- :scroll-top="scrollRightTop" -->
		<scroll-view scroll-y="true"  :scroll-into-view="clickId" :scroll-anchoring="true" scroll-with-animation class="classics-right" @scroll="scrollEvt">
			<view class="classics-right-item" v-for="item in contentData" :key="item.id" :id="item.id">
				<view class="title">
					{{item.title}}
				</view>
				<view class="item-box" v-for="it in item.content">
					<img class="item-box-left" :src="it.img" alt="">
					<view class="item-box-right">
						<view class="item-box-right-name">
							{{it.name}}
						</view>
						<view class="item-box-right-describe">
							{{it.desc}}
						</view>
						<view class="item-box-right-buy">
							<view class="item-box-right-price">{{it.price}}
							</view>
							<view class="item-box-right-pick">
								选规格
							</view>
						</view>
					</view>
				</view>
			</view>
		</scroll-view>
	</view>
</template>

js

<script>
	export default {
		components: {},
		data() {
			return {
				contentData:[{
						id:'tab1',
						title:'热销专区',
						content:[
							{id:101,img:'static/rexiao1.png',name:'郁金香',desc:'花语',price:'99',},
							{id:102,img:'static/rexiao2.png',name:'郁金香',desc:'花语',price:'99',},
						]
					},
					{
						id:'tab2',
						title:'生日鲜花',
						content:[
							{id:201,img:'static/shengri1.png',name:'郁金香',desc:'花语',price:'99',},
							{id:202,img:'static/shengri2.png',name:'郁金香',desc:'花语',price:'99',},
						]
					},......],//列表数据
				clickId:'tab1', //点击选项的id
				picked:0, // 左侧选中选项的index
				nowRightIndex:0, // 右边当前滚动的index
				itemArr:[], //用于存放右侧item位置数据
				scrollRightTop:0,
				timer:null,
			}
		},
		
		methods: {
			// 左侧切换点击事件
			selectActiveEvt(e) {
				this.clickId = e.id;
				this.picked = this.contentData.findIndex(it=>it.id==e.id);
			},
			scrollEvt(e){
				// 防抖
				if(this.timer){
					clearTimeout(this.timer);
				}
				this.timer = setTimeout(()=>{
					this.nowRightIndex =  this.itemArr.findLastIndex(it=>e.detail.scrollTop>=(it.bottom-228))+1; //判断当前顶部是处于哪个item,获取当前item的index
					if(this.nowRightIndex==-1) this.nowRightIndex=0;
					if(this.nowRightIndex==this.picked) return;
					this.clickId = this.contentData[this.nowRightIndex].id;
					this.picked = this.nowRightIndex;
				},500);
			},
			// 计算右侧每个item到顶部的距离,存放到数组
			getItemDistence(){
				new Promise(resolve=>{
					let selectQuery = uni.createSelectorQuery().in(this);
					selectQuery.selectAll('.classics-right-item').boundingClientRect(rect=>{
						if(!rect.length){
							setTimeout(()=>{
								this.getItemDistence();
							},10);
							return;
						}
						rect.forEach(it=>{
							this.itemArr.push(it); // 这里获取到的数据是每个item距离页面顶部的数据,以及每个item的自身数据
							resolve();
						})
					}).exec()
				})
			},
		},
		mounted(){
			// 设置一个延时,确保所有dom和样式加载完成,否则拿到的数据可能有误
			setTimeout(()=>{
				this.getItemDistence();
			},500)
			
		},
	}
</script>

css

	.sortBox{
		height: 100%;
		width: 100%;
		box-sizing: border-box;
		/* padding: 12px; */
		padding-bottom: 0;
		position: relative;
	}
	.boxTop{
		width: 100%;
		position: fixed;
		top: 44px;
		left: 0;
		height: 220px;
	}
	.sort-search{
		height: 50px;
		display: flex;
		justify-content: space-between;
		padding: 12px;
		padding-bottom: 0;
		box-sizing: border-box;
	}
	.sort-search-left{
		box-sizing: border-box;
		width: 100px;
		height: 38px;
		line-height: 38px;
		border-radius: 16px;
		border: 1px solid #f0f0f0;
		text-align: right;
		padding: 0 10px;
		background: url('../../static/ping.png') left center no-repeat;
		background-size: 45%;
	}
	.sort-search-right{
		width: 36px;
		height: 100%;
		margin-right: 80px;
		border: #f0f0f0 1px solid;
		border-radius: 19px;
		text-align: center;
		line-height: 36px;
	}
	.sort-collect{
		margin-top: 15px;
 
	}
	.sort-broadcast{
		width: 100%;
		height: 30px;
		background-color: #fff7fa;
		display: flex;
		font-size: 18px;
		line-height: 30px;
		padding: 0 10px;
		box-sizing: border-box;
		font-weight: bold;
		
	}
	.sort-type{
		display: flex;
		height: 60px;
		box-sizing: border-box;
		align-items: flex-end;
		justify-content: space-evenly;
		font-size: 16px;
		font-weight: bold;
		color: #909090;
	}
	.sort-type-item{
		display: flex;
		align-items: center;
		height: 40px;
	}
	.sort-type-item-img{
		height: 30px;
		margin-right: 6px;
	}
	.sort-type-item-text{
		height: 100%;
		line-height: 40px;
	}
	.sort-type-item-text-checked{
		border-bottom: #ff9092 3px solid;
		color: #41414d;
	}
	.opcity{
		opacity: 0;
	}
	.sortBox-scoll{
		position: absolute;
		top: 220px;
		left: 0;
		width: 100%;
		background-color: #f0f0f0;
		height: calc(100vh  - 314px);
		overflow-y: auto;
	}

2- 实现结果如下:
在这里插入图片描述

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

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

相关文章

三种方法实现获取链表中的倒数第n个元素

文章目录 先放初始代码方式1方式2方式3 先放初始代码 节点类 public class HeroNode {public int no;public String name;public HeroNode next; //指向下一个节点public HeroNode(int no, String name, HeroNode next) {this.no no;this.name name;this.next next;}Overr…

C语言——内存函数【memcpy,memmove,memset,memcmp】

&#x1f4dd;前言&#xff1a; 在之前的文章C语言——字符函数和字符串函数&#xff08;一&#xff09;中我们学习过strcpy和strcat等用来实现字符串赋值和追加的函数&#xff0c;那么除了字符内容&#xff0c;其他的数据&#xff08;例如整型&#xff09;能否被复制或者移动呢…

Linux centos stream9 parted

在Linux中&#xff0c;常用的磁盘管理工具包括 fdisk、parted、gdisk 等。它们可以用于创建、删除、调整分区、查看分区表等操作。 传统的MBR分区表(即主引导记录)大家都很熟悉&#xff0c;是过去我们使用windows时常见的。所支持的最大卷2T&#xff0c;且对分区有限制&#x…

微信号一天能加多少个好友?

微信的风控措施日益严格&#xff0c;因其本身是一款社交软件&#xff0c;却因流量巨大而被用于营销。若借助微信进行营销&#xff0c;仅依靠一个微信号&#xff0c;每日发送好友验证上限仅为20个&#xff08;针对老号&#xff09;&#xff0c;最佳数量控制在15个以内&#xff0…

DolphinScheduler伪集群部署

一.伪集群部署 伪集群部署目的是在单台机器部署 DolphinScheduler 服务&#xff0c;该模式下master、worker、api server、logger server都在同一台机器上。单机版本稳定性较差&#xff0c;官方建议20个以下流程使用。 二.前置需求 &#xff11;、&#xff12;.&#xff10;.…

Nightingale 夜莺监控系统 - 告警篇(3)

Author&#xff1a;rab 官方文档&#xff1a;https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v6/usage/alert/alert-rule/ 目录 前言一、配置1.1 创建钉钉机器人1.2 n9e 创建通知用户1.3 n9e 创建团队&#xff08;组&#xff09;1.4 将通知用户添加团队1.…

Python自动化我选DrissionPage,弃用Selenium

DrissionPage 是一个基于 python 的网页自动化工具。 它既能控制浏览器&#xff0c;也能收发数据包&#xff0c;还能把两者合而为一。 可兼顾浏览器自动化的便利性和 requests 的高效率。 它功能强大&#xff0c;内置无数人性化设计和便捷功能。 它的语法简洁而优雅&#x…

玩转Mysql 五(MySQL索引)

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。如有侵权&#xff0c;请留言&#xff0c;我及时删除&#xff01; 一、索引的数据结构 1、MySQL官方对索引的定义为&#xff1a;索引&#xff08;Index&#xff09;是…

【动态规划】【记忆化搜索】C++算法:546移除盒子

作者推荐 【动态规划】458:可怜的小猪 本文涉及知识点 动态规划 记忆化搜索 LeetCode546. 移除盒子 给出一些不同颜色的盒子 boxes &#xff0c;盒子的颜色由不同的正数表示。 你将经过若干轮操作去去掉盒子&#xff0c;直到所有的盒子都去掉为止。每一轮你可以移除具有相…

请查收“链上天眼”2023年成绩单

1月10日是中国人民警察节&#xff0c;是一份责任&#xff0c;更一份安心&#xff0c;随着科技的发展&#xff0c;链上安全领域的技术与工具不断更新迭代&#xff0c;更加安全的Web3世界正在构建。 根据欧科云链安全团队统计&#xff0c;2023 年全球范围内利用虚拟货币进行诈骗…

程序员如何弯道超车?周末有奇效

作为一名程序员&#xff0c;不断提升自己的技能和知识是至关重要的。然而&#xff0c;在繁忙的工作日常中&#xff0c;很难有足够的时间和精力来学习新技术或深入研究。因此&#xff0c;周末成为了一个理想的时机&#xff0c;可以专注于个人发展和技能提升。所以程序员如何利用…

QT周五作业

题目&#xff1a;实现简单水果的价格重量计算 点击一次水果重量1 自动计算总价 代码&#xff1a; widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QListWidgetItem> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAME…

从零开发短视频电商 PaddleOCR Java推理 (一)飞桨引擎推理

文章目录 简介方式一&#xff1a;DJL 飞浆引擎 飞桨模型方式二&#xff1a;ONNXRuntime 飞桨转换后的ONNX模型&#xff08;Paddle2ONNX&#xff09; 添加依赖文字识别OCR过程分析文字区域检测文字角度检测文字识别&#xff08;裁减旋转后的文字区域&#xff09; 高级替换模型…

【PHP AES加解密示例】从入门到精通,一篇文章让你掌握加密解密技术!

一、引言 随着互联网的普及&#xff0c;数据安全问题越来越受到人们的关注。在众多加密算法中&#xff0c;AES&#xff08;Advanced Encryption Standard&#xff09;因其高效、安全的特点被广泛应用。本文将通过PHP语言&#xff0c;为大家展示一个简单的AES加解密示例&#x…

Javaweb的网络投票系统的设计与实现

目的与意义 原始的投票管理基本上是人工操作&#xff0c;效率低下&#xff0c;缺乏方便性&#xff0c;网上投票管理系统运用计算机和其他附加设备&#xff0c;不再需要手工操作&#xff0c;基本上是全自动化&#xff0c;能够节省人力&#xff0c;大大的提高了效率。使投票变得…

ChromeDriver 添加到系统PATH

在软件测试和自动化脚本中&#xff0c;ChromeDriver是一个不可或缺的工具。为了方便使用&#xff0c;将其添加到系统PATH中是一个明智的选择。以下是在Windows、macOS或Linux上完成此任务的详细步骤。 在 Windows 上&#xff1a; 下载并保存 ChromeDriver&#xff1a; 访问Chro…

【学习】FPGA verilog 编程使用vscode,资源占用多 卡顿 卡死 内存占用多解决方案

问题描述 FPGA verilog 编程使用vscode&#xff0c;资源占用多 卡顿 卡死 内存占用多解决方案。 32G内存&#xff0c;动不动就暂用50%&#xff01;&#xff01; 解决方案 1.打开设置 文件->首选项->设置 或者点击软件界面的左下角的齿轮按钮 2.进入如下【设置】界面 …

springcloud-cloud provider-payment8001微服务提供者支付Module模块

文章目录 IDEA新建project工作空间cloud-provider-payment8001微服务提供者支付Module模块建表SQL测试 IDEA新建project工作空间 微服务cloud整体聚合父工程Project 写pom文件 <?xml version"1.0" encoding"UTF-8"?><project xmlns"htt…

JVM知识总结(持续更新)

这里写目录标题 java内存区域程序计数器虚拟机栈本地方法栈堆方法区 java内存区域 Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域&#xff1a; 程序计数器虚拟机栈本地方法栈堆方法区 程序计数器 记录下一条需要执行的虚拟机字节码指令…

安全漏洞周报(2024.01.01-2023.01.08)

漏洞速览 ■ 用友CRM系统存在逻辑漏洞 漏洞详情 1. 用友CRM系统存在逻辑漏洞 漏洞介绍&#xff1a; 某友CRM系统是一款综合性的客户关系管理软件&#xff0c;旨在帮助企业建立和维护与客户之间的良好关系。它提供了全面的功能&#xff0c;包括销售管理、市场营销、客户服…