vue3简单写导航anchor示例(支持点击高亮和滚动判断高亮)

1.  点击anchor, 相应的anchorlink高亮

function anchorClick(index) {
  forceStop.value = true;
  time = Date.now();
  wheelRef.value.children[index].scrollIntoView({
    block: 'start',
    behavior: 'smooth'
  });
  // 给一些延时, 再点亮anchor, 同时不再限制scroll事件函数里面滚动高亮的判断
  setTimeout(() => {
    forceStop.value = false;
    time = null;
    currentIndex.value = index;
  }, 100 * Math.abs(currentIndex.value - index) > 1000
    ? 1000
    : 100 * Math.abs(currentIndex.value - index));
}

2. scroll页面, 根据列表的容器高度和滚动块之间的数值关系判断anchor高亮:

//滚动的函数
function handleScroll(e) {time && console.log((Date.now() - time) / 1000, '滚动间隔时间', forceStop.value)if (forceStop.value) {return;}const scrollingElement = e.target;const scrollTop = scrollingElement.scrollTop;const headerOffsetTop = headerRef.value.offsetTop;const headerOffsetHeight = headerRef.value.offsetHeight;const navOffsetTop = navRef.value.offsetTop;const navOffsetHeight = navRef.value.offsetHeight;const windowClientHeight = scrollingElement.clientHeight;const windowScrollHeight = scrollingElement.scrollHeight;// 如果滚动元素的scrollTop比header元素的高度+offsetTop还大, 就让nav部分悬停在顶部!!!if (scrollTop >= headerOffsetHeight + headerOffsetTop) {// 因为nav悬停了, 所以scrollTop - header的高度就是判断靠近顶部窗口的可见的list内容了, 从而和anchorlink的高亮产生联系const gap = scrollTop - headerOffsetHeight;const idx = _.findIndex(listData1, ee => {const a = _.get(ee, 'listItemsHeightArrs');if (gap >= a[0] && gap < a[1]) {return ee}})currentIndex.value = idx;isFixed.value = true;} else {isFixed.value = false;currentIndex.value = 0;}// 滑到底部if (windowClientHeight + scrollTop === windowScrollHeight) {currentIndex.value = listData1.length - 1;}
}

3. 完整示例代码:

<template><div class="fixed-top-container" :ref="scrollWrapperRef"><header class="header" :ref="headerRef">头部</header><nav class="fixed-top-nav" :ref="navRef" :class="{ isFixed }"><div class="box" v-for="(item, index) in navData" :key="index">{{ item.title }}</div></nav><ul class="fixed-top-list" :ref="wheelRef"><li v-for="(item, index) in listData1">{{ item.name }}<ul><li class="list-item" v-for="(item, index) in item.list">{{ item.text }}</li></ul></li></ul><ul class="anchor-conatiner"><li v-for="(item, index) in listData1" :class="currentIndex === index ? 'current' : ''" @click="anchorClick(index)">{{ item.name }}</li></ul></div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted, onBeforeUnmount, nextTick, Ref } from 'vue';
import _ from 'lodash';const isFixed = ref(false); //是否固定的
const headerRef = ref('headerRef') as Ref;
const navRef = ref('navRef') as Ref;
const wheelRef = ref('wheelRef') as Ref;
const currentIndex = ref(0);
const forceStop = ref(false);
const scrollWrapperRef = ref('scrollWrapperRef') as Ref;
let time: any = null// mock数据-----------------------start--------------
const navData = reactive([{ title: 'navRef', id: 1 },{ title: 'nav2', id: 2 },{ title: 'nav3', id: 3 },{ title: 'nav4', id: 4 },
]);const arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N'];
let sum = 0;
const listData1 = reactive(Array.from({ length: arr.length }, (item, index) => {const list = Array.from({ length: 5 }, (item, i) => ({id: 'list-item-' + i + 1,text: 'list-item-text-' + i,name: 'list-name-' + i,}));const sum1 = sumsum += 40 * (list.length + 1)return {listItemsHeightArrs: [sum1, sum], // 前一个标题内的list内容累计高度, 当前标题内的内容累计高度name: arr[index] + '-累计高度为:' + JSON.stringify([sum1, sum]),list,}
}));
// mock数据-----------------------end--------------function anchorClick(index) {forceStop.value = true;time = Date.now();wheelRef.value.children[index].scrollIntoView({block: 'start',behavior: 'smooth'});// 给一些延时, 再点亮anchor, 同时不再限制scroll事件函数里面滚动高亮的判断setTimeout(() => {forceStop.value = false;time = null;currentIndex.value = index;}, 100 * Math.abs(currentIndex.value - index) > 1000? 1000: 100 * Math.abs(currentIndex.value - index));
}//滚动的函数
function handleScroll(e) {time && console.log((Date.now() - time) / 1000, '滚动间隔时间', forceStop.value)if (forceStop.value) {return;}const scrollingElement = e.target;const scrollTop = scrollingElement.scrollTop;const headerOffsetTop = headerRef.value.offsetTop;const headerOffsetHeight = headerRef.value.offsetHeight;const navOffsetHeight = navRef.value.offsetHeight;const windowClientHeight = scrollingElement.clientHeight;const windowScrollHeight = scrollingElement.scrollHeight;// 如果滚动元素的scrollTop比header元素的高度+offsetTop还大, 就让nav部分悬停在顶部!!!if (scrollTop >= headerOffsetHeight + headerOffsetTop) {// 因为nav悬停了, 所以scrollTop - header的高度就是判断靠近顶部窗口的可见的list内容了, 从而和anchorlink的高亮产生联系const gap = scrollTop - headerOffsetHeight;const idx = _.findIndex(listData1, ee => {const a = _.get(ee, 'listItemsHeightArrs');if (gap >= a[0] && gap < a[1]) {return ee}})currentIndex.value = idx;isFixed.value = true;} else {isFixed.value = false;currentIndex.value = 0;}// 滑到底部if (windowClientHeight + scrollTop === windowScrollHeight) {currentIndex.value = listData1.length - 1;}
}onMounted(() => {nextTick(() => {scrollWrapperRef.value.addEventListener('scroll', handleScroll, false);});
})onBeforeUnmount(() => { // 页面即将销毁取消事件监听scrollWrapperRef.value.removeEventListener('scroll', handleScroll);
})
</script>
<style scoped lang="scss">
* {margin: 0;padding: 0;
}.fixed-top-container {height: 100vh;overflow: auto;& .header {height: 200px;width: 100%;background-color: #ff5555;}& .fixed-top-nav {display: flex;width: 100%;background-color: #f90;&.isFixed {position: fixed;left: 0;top: 0;z-index: 999;}& .box {font-size: 14px;height: 30px;line-height: 30px;color: #333;flex: 1 1 0%;}}& .fixed-top-list {list-style: none;font-size: 16px;line-height: 40px;&>li {background-color: green;}& li {box-sizing: border-box;}& .list-item {width: 100%;height: 40px;line-height: 40px;font-size: 16px;border-bottom: 1px solid #333;background-color: #fff;}}.anchor-conatiner {position: fixed;top: 10%;right: 10px;& li {font-size: 14px;&.current {color: red;}&.light {color: green;}}}
}
</style>

4. 如果不让nav部分悬停:

<template><div class="fixed-top-container" :ref="scrollWrapperRef"><header class="header" :ref="headerRef">头部</header><nav class="fixed-top-nav" :ref="navRef"><div class="box" v-for="(item, index) in navData" :key="index">{{ item.title }}</div></nav><ul class="fixed-top-list" :ref="wheelRef"><li v-for="(item, index) in listData1">{{ item.name }}<ul><li class="list-item" v-for="(item, index) in item.list">{{ item.text }}</li></ul></li></ul><ul class="anchor-conatiner"><li v-for="(item, index) in listData1" :class="currentIndex === index ? 'current' : ''" @click="anchorClick(index)">{{ item.name }}</li></ul></div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted, onBeforeUnmount, nextTick, Ref } from 'vue';
import _ from 'lodash';const headerRef = ref('headerRef') as Ref;
const navRef = ref('navRef') as Ref;
const wheelRef = ref('wheelRef') as Ref;
const currentIndex = ref(0);
const forceStop = ref(false);
const scrollWrapperRef = ref('scrollWrapperRef') as Ref;
let time: any = null// mock数据-----------------------start--------------
const navData = reactive([{ title: 'navRef', id: 1 },{ title: 'nav2', id: 2 },{ title: 'nav3', id: 3 },{ title: 'nav4', id: 4 },
]);const arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N'];
let sum = 0;
const listData1 = reactive(Array.from({ length: arr.length }, (item, index) => {const list = Array.from({ length: 5 }, (item, i) => ({id: 'list-item-' + i + 1,text: 'list-item-text-' + i,name: 'list-name-' + i,}));const sum1 = sumsum += 40 * (list.length + 1)return {listItemsHeightArrs: [sum1, sum], // 前一个标题内的list内容累计高度, 当前标题内的内容累计高度name: arr[index] + '-累计高度为:' + JSON.stringify([sum1, sum]),list,}
}));
// mock数据-----------------------end--------------function anchorClick(index) {forceStop.value = true;time = Date.now();wheelRef.value.children[index].scrollIntoView({block: 'start',behavior: 'smooth'});// 给一些延时, 再点亮anchor, 同时不再限制scroll事件函数里面滚动高亮的判断setTimeout(() => {forceStop.value = false;time = null;currentIndex.value = index;}, 100 * Math.abs(currentIndex.value - index) > 1000? 1000: 100 * Math.abs(currentIndex.value - index));
}//滚动的函数
function handleScroll(e) {time && console.log((Date.now() - time) / 1000, '滚动间隔时间', forceStop.value)if (forceStop.value) {return;}const scrollingElement = e.target;const scrollTop = scrollingElement.scrollTop;const headerOffsetTop = headerRef.value.offsetTop;const headerOffsetHeight = headerRef.value.offsetHeight;const navOffsetTop = navRef.value.offsetTop;const navOffsetHeight = navRef.value.offsetHeight;const windowClientHeight = scrollingElement.clientHeight;const windowScrollHeight = scrollingElement.scrollHeight;// navOffsetTop-headerOffsetTop就是header的高度, 还需要加上nav的高度才是list内容上面的块的高度const gap = scrollTop - (navOffsetTop-headerOffsetTop+navOffsetHeight);if (gap >= 0) {const idx = _.findIndex(listData1, ee => {const a = _.get(ee, 'listItemsHeightArrs');if (gap >= a[0] && gap < a[1]) {return ee}})currentIndex.value = idx;}else {currentIndex.value = 0;}// 滑到底部if (windowClientHeight + scrollTop === windowScrollHeight) {currentIndex.value = listData1.length - 1;}
}onMounted(() => {nextTick(() => {scrollWrapperRef.value.addEventListener('scroll', handleScroll, false);});
})onBeforeUnmount(() => { // 页面即将销毁取消事件监听scrollWrapperRef.value.removeEventListener('scroll', handleScroll);
})
</script>
<style scoped lang="scss">
* {margin: 0;padding: 0;
}.fixed-top-container {height: 100vh;overflow: auto;& .header {height: 200px;width: 100%;background-color: #ff5555;}& .fixed-top-nav {display: flex;width: 100%;background-color: #f90;& .box {font-size: 14px;height: 30px;line-height: 30px;color: #333;flex: 1 1 0%;}}& .fixed-top-list {list-style: none;font-size: 16px;line-height: 40px;&>li {background-color: green;}& li {box-sizing: border-box;}& .list-item {width: 100%;height: 40px;line-height: 40px;font-size: 16px;border-bottom: 1px solid #333;background-color: #fff;}}.anchor-conatiner {position: fixed;top: 10%;right: 10px;& li {font-size: 14px;&.current {color: red;}&.light {color: green;}}}
}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windows下基于vscode的ssh服务远程连接ubuntu服务器

Ubuntu端配置 1.确保ubuntu端已启用ssh服务 首先&#xff0c;安装ssh服务 sudo apt-get install openssh-server 安装后&#xff0c;打开ssh服务 sudo service ssh start 如果显示有sshd就说明成功了。 判断是否成功打开 ps -e|grep ssh 同时也可以通过如下方式确保ss…

小样本分割的新视角,Learning What Not to Segment【CVPR 2022】

论文地址&#xff1a;Excellent-Paper-For-Daily-Reading/image-segmentation at main 类别&#xff1a;图像分割 时间&#xff1a;2023/11/01 摘要 目前背景&#xff1a;少样本分割 &#xff08;FSS&#xff09; 得到了广泛的发展。以前的大多数工作都在努力通过分类任务衍…

Flask 网站装潢, 简易更换模板

Flask 网站装潢&#xff0c;简易更换模板 本博文找个好看的网页模板&#xff0c;并简单改一改变成flask模板&#xff0c;并展示 主博客目录&#xff1a;《从零开始学习搭建量化平台笔记》 文章目录 Flask 网站装潢&#xff0c;简易更换模板下载模板Python 自动生成目录修改目录…

pytorch复现_IOU

定义了一个compute_iou函数&#xff0c;用于计算两个矩形框&#xff08;boxA和boxB&#xff09;之间的交并比&#xff08;IOU&#xff0c;Intersection over Union&#xff09;。IOU是一种常用的度量&#xff0c;用于评估两个矩形框的重叠程度。 在代码中&#xff0c;函数的输入…

centos关闭Java进程的脚本

centos关闭Java进程的脚本&#xff0c;有时候服务就是个jar包&#xff0c;关闭程序又要找到进程ID&#xff0c;在kill掉&#xff0c;麻烦&#xff0c;这里就写了个脚本 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.脚本如下 #!/bin/bash ps -ef | grep java | gre…

什么是 CNN? 卷积神经网络? 怎么用 CNN 进行分类?(3)

参考视频&#xff1a;https://www.youtube.com/watch?vE5Z7FQp7AQQ&listPLuhqtP7jdD8CD6rOWy20INGM44kULvrHu 视频7&#xff1a;CNN 的全局架构 卷积层除了做卷积操作外&#xff0c;还要加上 bias &#xff0c;再经过非线性的函数&#xff0c;这么做的原因是 “scaled p…

《 博弈论教程(罗云峰版) 》——习题二答案

前言 博弈论这门课程&#xff0c;我们主要参考的教材是《博弈论教程&#xff08;罗云峰版&#xff09;》&#xff0c;但是罗老师的课后习题并没有给出完整的答案&#xff0c;秉着学习的态度&#xff0c;本人结合教材和 PPT 在这里给出课后习题的答案。 由于我们只学了完全信息…

sw 怎么装新版本

我们在安装solidworks时&#xff0c;有时候会提示A newer version of this applic ation is already installed. Installation stopped.如下图所示 这时候需要点继续安装 然后会出现下图所示情况&#xff0c;vba7.1安装未成功 这是因为我们电脑中以前安装过更高版本的solidw…

安装opensips

1. 安装opensips ubuntu下安装&#xff1a; 1&#xff09;执行以下的脚本 openSIPS | APT Repository 2&#xff09;apt-get install opensips 安装完毕后&#xff0c;再选择需要的module继续安装&#xff0c;不需要编译 如果只是用作load balancer&#xff0c;那么只需要…

arcpy.message实现探索

arcpy 位置D:\Program Files\GeoScene\Pro\Resources\ArcPy\arcpy\__init__.py ”““AddMessage(消息) 创建可以使用任何GetMessages函数访问的地理处理信息消息(Severity0)。 message(字符串):要添加的消息。”“ arcpy.geoprocessing D:\Program Files\GeoScene\Pro\Re…

5.4 完整性约束命名子句

思维导图: 笔记&#xff1a;5.4 完整性约束命名子句 定义: 完整性约束是在CREATE TABLE语句中定义的。SQL为CREATE TABLE语句提供了CONSTRAINT子句&#xff0c;用于对完整性约束进行命名。命名的目的是方便增加或删除约束。 基本结构: CONSTRAINT <完整性约束名称> &l…

python实现MC协议(SLMP 3E帧)的TCP服务端(篇一)

python实现MC协议&#xff08;SLMP 3E帧&#xff09;的TCP服务端是一件稍微麻烦点的事情。它不像modbusTCP那样&#xff0c;可以使用现成的pymodbus模块去实现。但是&#xff0c;我们可以根据协议帧进行组包&#xff0c;自己去实现帧的格式&#xff0c;而这一切可以基于socket模…

jdk官网下载(详细步骤)

jdk全部版本下载网址 Java Archive | Oraclehttps://www.oracle.com/java/technologies/downloads/archive/ 下载之前先建立oracle账号(免费创建)&#xff0c;不用特意去搜&#xff0c;你点击下载jdk的时候会自动弹出来&#xff0c;自己建立一个账号就能下载了 找到自己要下载…

C++数据结构算法篇Ⅰ

C数据结构算法篇Ⅰ &#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C算法 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 主要内容讲解数据结构中的链表结构 文章目录 C数据…

PHP服务器端电商API原理及示例讲解(电商接口开发/接入)

下面小编就为大家分享一篇PHP服务器端API原理及示例讲解(接口开发)&#xff0c;具有很好的参考价值&#xff0c;希望对大家有所帮助 相信大家都做过PHP请求电商API接口获取数据&#xff0c;比如淘宝平台商品API接口&#xff0c;订单接口&#xff0c;京东接口&#xff0c;1688接…

Python画图之皮卡丘

Python-turtle画出皮卡丘&#xff08;有趣小游戏&#xff09; 一、效果图二、Python代码 一、效果图 二、Python代码 import turtledef getPosition(x, y):turtle.setx(x)turtle.sety(y)print(x, y)class Pikachu:def __init__(self):self.t turtle.Turtle()t self.tt.pensi…

Android广播BroadcastReceiver

BroadcastReceiver组件 BroadcastReceiver是Android中的一个组件&#xff0c;用于接收和处理系统广播或应用内广播。它可以监听系统事件或应用内自定义的广播&#xff0c;并在接收到广播时执行相应的操作。 广播是一种用于在应用组件之间传递消息的机制。通过发送广播&#x…

如何使用查看器筛选、搜索功能进行数据定位?

前言 我们曾探讨过观测云如何通过将内置视图与查看器相联结&#xff0c;实现更全面的数据关联分析。&#xff08;参见《内置视图联动查看器&#xff0c;实现数据关联分析》&#xff09;这里提到的查看器&#xff0c;实际是一个功能全面且强大的数据查看分析工具。其提供多种搜…

土壤数据库辅助工具SPAW计算土壤导水率

土壤数据库辅助工具SPAW 首先下载SPAW工具 点击打开 根据之前的1比100土壤数据查表得到各个组分含量 其中 Field Capacity是田间持水量 Matric Bulk Density是基质粒密度 参考文章 【SWAT水文模型】ArcSWAT土壤数据库辅助工具SPAW简述

Security ❀ DNS协议常见DOS攻击详解

文章目录 1. DNS协议基础概述2. DNS报文详解2.1. DNS Request 请求包2.2. DNS Reply 响应包 3. DNS Request Flood3.1. 攻击原理3.2. 防护方法3.2.1. TC源认证3.2.2. 被动防御3.2.3. CNAME防护模式3.2.4. *CANME类型解析过程** 4. DNS Reply Flood4.1. 攻击原理4.2. 防护方法 5…