vue+IntersectionObserver + scrollIntoView 实现电梯导航

一、电梯导航

电梯导航也被称为锚点导航,当点击锚点元素时,页面内相应标记的元素滚动到视口。而且页面内元素滚动时相应锚点也会高亮。电梯导航一般把锚点放在左右两侧,类似电梯一样。

二、scrollIntoView() 介绍

scrollIntoView() 方法会滚动元素的父容器,使元素出现在可视区域。默认是立即滚动,没有动画效果。

如果要添加动画效果,可以这么做:

scrollIntoView({ behavior: 'smooth' // instant 为立即滚动 })

它还有两个可选参数 blockinline

block 表示元素出现在视口时垂直方向与父容器的对齐方式,

inline 表示元素出现在视口时水平方向与父容器的对齐方式。

他们同样都有四个值可选 startcenterend 、nearest。默认为 start;

scrollIntoView({ behavior: 'smooth', block:'center', inline:'center', })

对于 block

  • start  将元素的顶部和滚动容器的顶部对齐。

  • center  将元素的中心和滚动容器的中心垂直对齐。

  • end  将元素的底部和滚动容器的底部对齐。

对于 inline

  • start 将元素的左侧和滚动容器的左侧对齐。

  • center  将元素的中心和滚动容器的中心水平对齐。

  • end  将元素的右侧和容器的右侧对齐。

nearest 不论是垂直方向还是水平方向,只要出现在视口任务就完成了。可以理解为以最小移动量让元素出现在视口,(慵懒移动)。如果元素已经完全出现在视口中,则不会发生变化。

在这里可以查看这个完整例子 scrollIntoView 可选项参数实践(codepen)

而且 scrollIntoView 兼容性也很好

三、IntersectionObserver 介绍

Intersection Observer API(交叉观察器 API) 提供了一种异步检测目标元素与祖先元素或顶级文档的视口相交情况变化的方法。也就是能判断元素是否在视口中,并且能监听元素在视口中出现的可见部分的比例,从而可以执行我们自定义的逻辑。

由于是异步,也就不会阻塞主线程,性能自然比之前的频繁执行 getBoundingClientRect() 判断元素是否在视口中要好。

创建一个 IntersectionObserver

let options = {

 root: document.querySelector(selector),

 rootMargin: "0",

 threshold: 1.0,

};

let observer = new IntersectionObserver(callback, options);

let target = document.querySelector(selector);

observer.observe(target); //监听目标元素

通过调用 IntersectionObserver 构造函数可以创建一个交叉观察器,构造函数接收两个参数,一个回调函数和一个可选项。上面例子中,当元素完全出现(100%)在视口中时会调用回调函数。

 可选项

  • root 用作视口的元素,必须是目标的祖先。默认为浏览器视口。

  • rootMargin 根周围的边距,也就是可以限制根元素检测视口的大小。值的方向大小和平常用的 margin 一样,例如 "10px 20px 30px 40px"(上、右、下、左)。只不过正数是增大根元素检测范围,负数是减小检测范围。

比如设置一个可以滚动的 div 容器为根元素,宽高都为1000px。 此时设置 rootMargin:0 表示根元素检测视口大小就是当前根元素可视区域大小,也就是 1000px * 1000px。设置 rootMargin:25% 0 25% 0 表示上下边距为 25%,那么检测视口大小就是 1000px * 500px。

  • threshold 一个数字或一个数字数组,表示目标出现在视口中达到多少百分比时,观察器的回调就应该执行。如果只想在能见度超过 50% 时检测,可以使用 0.5 的值。如果希望每次能见度超过 25% 时都执行回调,则需要指定数组 [0, 0.25, 0.5, 0.75, 1]。默认值为 0(这意味着只要有一个像素可见,回调就会运行)。值为 1.0 意味着在每个像素都可见之前,阈值不会被认为已通过。

回调函数

当目标元素匹配了可选项中的配置后,会触发我们定义的回调函数

let options = {

 root: document.querySelector(selector),

 rootMargin: "0",

 threshold: 1.0,

};

let observer = new IntersectionObserver(function (entries) {   entries.forEach(entry => {

 })

}, options);

entries 表示被监听目标元素组成的数组,数组里面每个 entry 都有下列一些值

  • entry.boundingClientRect 返回目标元素的边界信息,值和 getBoundingClientRect() 形式一样。

  • entry.intersectionRatio 目标元素和根元素交叉的比例,也就是出现在检测区域的比例。

  • entry.intersectionRect 返回根和目标元素的相交区域的边界信息,值和 getBoundingClientRect() 形式一样。

  • entry.isIntersecting 返回true或者fasle,表示是否出现在根元素检测区域内

  • entry.rootBounds 返回根元素的边界信息,值和 getBoundingClientRect() 形式一样。

  • entry.target 返回出现在根元素检测区域内的目标元素

  • entry.time 返回从交叉观察器被创建到目标元素出现在检测区域内的时间戳

比如,要检测目标元素有75%出现在检测区域中就可以这样做:

entries.forEach(entry => {

 if(entry.isIntersecting && entry.intersectionRatio>0.75){

 }

})

监听目标元素

创建一个观察器后,对一个或多个目标元素进行观察。

//单个元素监听

let target = document.querySelector(selector);

observer.observe(target);

//多个元素监听

document.querySelectorAll('div').forEach(el => {

 observer.observe(el)

})

掌握了 IntersectionObserver + scrollIntoView 的用法,实现电梯导航就简单了。

四、代码实现

<template>

  <div class="navigation">

    <div class="navigation_left">

      <div v-for="(v, i) in list" :key="i" :id="i" class="navigation_left_box">

        <h1 :id="v.href">{{ v.name }}</h1>

        <div class="box">

          <div v-for="(v1, i1) in v.children" :key="i1" class="box_item">

            {{ v1.name }}

          </div>

        </div>

      </div>

    </div>

    <div class="navigation_right">

      <div class="box">

        <a class="box_item" :class="listIndex == i ? 'active' : ''" @click="scrollToAnchor(v.href, i)"

          v-for="(v, i) in list" :key="i">{{ v.name }}</a>

      </div>

    </div>

  </div>

</template>

<script>

export default {

  data () {

    return {

      list: [

        {

          name: '一、标题1',

          href: '#t1',

          children: [

            { name: '内容1' },

            { name: '内容2' },

            { name: '内容3' },

            { name: '内容4' },

            { name: '内容5' },

            { name: '内容6' },

            { name: '内容7' },

            { name: '内容8' },

            { name: '内容9' },

            { name: '内容10' },

            { name: '内容11' },

          ]

        },

        {

          name: '二、标题2',

          href: '#t2',

          children: [

            { name: '内容1' },

            { name: '内容2' },

            { name: '内容3' },

            { name: '内容4' },

            { name: '内容5' },

            { name: '内容6' },

            { name: '内容7' },

            { name: '内容8' },

            { name: '内容9' },

            { name: '内容10' },

          ]

        },

        {

          name: '三、标题3',

          href: '#t3',

          children: [

            { name: '内容1' },

            { name: '内容2' },

            { name: '内容3' },

            { name: '内容4' },

            { name: '内容5' },

            { name: '内容6' },

            { name: '内容7' },

            { name: '内容8' },

            { name: '内容9' },

            { name: '内容10' },

          ]

        },

        {

          name: '四、标题4',

          href: '#t4',

          children: [

            { name: '内容1' },

            { name: '内容2' },

            { name: '内容3' },

            { name: '内容4' },

            { name: '内容5' },

            { name: '内容6' },

            { name: '内容7' },

            { name: '内容8' },

            { name: '内容9' },

            { name: '内容10' },

          ]

        },

        {

          name: '五、标题5',

          href: '#t5',

          children: [

            { name: '内容1' },

            { name: '内容2' },

            { name: '内容3' },

            { name: '内容4' },

            { name: '内容5' },

            { name: '内容6' },

            { name: '内容7' },

            { name: '内容8' },

            { name: '内容9' },

            { name: '内容10' },

          ]

        },

        {

          name: '六、标题6',

          href: '#t6',

          children: [

            { name: '内容1' },

            { name: '内容2' },

            { name: '内容3' },

            { name: '内容4' },

            { name: '内容5' },

            { name: '内容6' },

            { name: '内容7' },

            { name: '内容8' },

            { name: '内容9' },

            { name: '内容10' },

          ]

        }

      ],

      listIndex: 0,

    }

  },

  mounted () {

    // 创建一个IntersectionObserver实例

    const observer = new IntersectionObserver((entries) => {

      for (let index = 0; index < entries.length; index++) {

        if (entries[index].isIntersecting) {

          console.log(entries[index]);

          // 目标元素进入视窗

          // 根据监听元素的属性id来给右侧的元素选中

          this.listIndex = entries[index].target.id

        }

      }

    },

    {

      // root 用作视口的元素,必须是目标的祖先。默认为浏览器视口。

      // hreshold 一个数字或一个数字数组,表示目标出现在视口中达到多少百分比时

      // 值为 1.0 意味着在每个像素都可见之前,阈值不会被认为已通过。

      threshold: 1

    }

    );

    // 选择所有需要观察的元素,并开始观察它们

    const elementsToObserve = document.querySelectorAll('.navigation_left_box');

    elementsToObserve.forEach(element => {

      //监听目标元素

      observer.observe(element);

    });

  },

  methods: {

    scrollToAnchor (anchorId, i) {

      this.listIndex = i

      const element = document.getElementById(anchorId);

      if (element) {

        // 滚动动画  滚动到目前位置

        element.scrollIntoView({ behavior: 'smooth' });

      }

    }

  },

}

</script>

<style lang="scss"  scoped>

.navigation {

  position: relative;

  display: flex;

  .navigation_left {

    flex: 1;

    h1 {

      padding: 10px;

    }

    .box {

      display: flex;

      flex-wrap: wrap;

      .box_item {

        width: 30%;

        height: 100px;

        border-radius: 4px;

        background-color: #E4CCFF;

        line-height: 100px;

        text-align: center;

        font-size: 20px;

        font-weight: 500;

        box-sizing: border-box;

        --n: 3;

        /* 一行几个 */

        --space: calc(100% - var(--n) * 30%);

        /* 一行减去item的宽度后剩下的间距 */

        --leftRight: calc(var(--space) / var(--n) / 2);

        /* 每个item左右的间距 */

        margin: 10px var(--leftRight);

      }

    }

  }

  .navigation_right {

    width: 200px;

    border-left: solid 1px #eee;

    position: relative;

    .box {

      position: fixed;

      top: 70px;

      width: 200px;

      padding-top: 10px;

      z-index: 999;

      .box_item {

        display: block;

        cursor: pointer;

        font-size: 16px;

        padding: 10px 5px;

        text-align: center;

      }

      .box_item:hover {

        background-color: #d5d5d54a;

      }

      .active {

        background-color: #d5d5d54a;

      }

    }

  }

}

</style>

五、效果展示 

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

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

相关文章

erlang学习: Mnesia Erlang数据库2

Mnesia数据库增加与查询学习 -module(test_mnesia).-record(shop, {item, quantity, cost}). -record(cost, {name, price}). -record(design, {info, plan}). %% API -export([insert/3,select/1,start/0]). start() ->mnesia:start().insert(Name, Quantity, Cost) ->…

k8s的Ingress控制器安装

Ingress文档地址&#xff1a;Ingress文档 1.安装helm 官网地址&#xff1a;helm官网安装 wget https://get.helm.sh/helm-v3.2.3-linux.amd64.tar.gz tar -zxvf helm-v3.2.3-linux-amd64.tar.gz cp linux-amd64/helm /usr/local/bin/ rootmaster01:~# helm version version.B…

《python语言程序设计》2018版第8章第14题金融:信用卡号合法性 利用6.29题

一、之前6.29题我做的代码 这是用数字来进行分辨的 is_txt 4383576018402626 #合法def split_the_data_even(vis_n):current_a1 vis_n // 10000a_t1 vis_n % 10000# print("1th", a_t1)a_t2 current_a1 % 10000# print("2th", a_t2)current_a3 curre…

Python设计模式实战:开启软件设计的精进之旅

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

python绘制3D瀑布图

成品&#xff1a; 代码&#xff1a; def line_3d(x, y, z, x_label_indexs):"""在y轴的每个点&#xff0c;向x轴的方向延伸出一个折线面&#xff1a;展示每个变量的时序变化。x: x轴&#xff0c;时间维&#xff0c;右边。y: y轴&#xff0c;变量维&#xff0c;…

《OpenCV计算机视觉》—— 图像形态学(腐蚀、膨胀等)

文章目录 一、图像形态学基本概念二、基本运算1.简单介绍2.代码实现 三、高级运算1.简单介绍2.代码实现 一、图像形态学基本概念 图像形态学是图像处理科学的一个独立分支&#xff0c;它基于集合论和数学形态学的理论&#xff0c;专门用于分析和处理图像中的形状和结构。图像形…

linux学习之线程2:线程控制与使用

铺垫 之前我们提到&#xff0c;Linux不直接对线程进行调度&#xff0c;而是对轻量级进程进行调度。但用户就想像Windows那样直接对线程进程控制。所以&#xff0c;就有了pthread库来封装了一层。 那么想要进行线程控制&#xff0c;要用pthread库。&#xff08;pthread库是原生…

Spire.PDF for .NET【文档操作】演示:创比较 PDF 文档

PDF 已成为跨不同平台共享和保存文档的标准格式&#xff0c;在专业和个人环境中都发挥着无处不在的作用。但是&#xff0c;创建高质量的 PDF 文档需要多次检查和修订。在这种情况下&#xff0c;了解如何有效地比较 PDF 文件并找出它们的差异变得至关重要&#xff0c;这使文档编…

《Python编程:从入门到实践》外星人入侵

一、规划 在游戏《外星人入侵》中&#xff0c;玩家控制着一艘最初出现在屏幕底部中央的飞船。玩家可以使用箭头键左右移动飞船&#xff0c;还可使用空格键进行射击。游戏开始时&#xff0c;一群外星人出现在天空中&#xff0c;他们在屏 幕中向下移动。玩家的任务是射杀这些外星…

潘多拉的盒子还是阿拉丁的神灯:揭示RAG噪声在大语言模型中的作用

一、结论写在前面 论文来自清华大学、北京国家信息科学与技术研究中心 论文标题&#xff1a;Pandora’s Box or Aladdin’s Lamp: A Comprehensive Analysis Revealing the Role of RAG Noise in Large Language Models 论文链接&#xff1a;https://arxiv.org/pdf/2408.135…

【包教包会】CocosCreator3.x拖尾MotionStreak威力加强版(支持3.x、支持原生、可合批)

将去年写的2.x拖尾升级到3.x 完美适配Web、原生平台&#xff08;其余平台没测过&#xff09;。 保留原版功能&#xff08;拖尾会跟随节点位移、缩放、受节点透明度影响&#xff0c;但不会跟随节点旋转&#xff09; 支持世界坐标 / 本地坐标切换&#xff08;至于为什么需要这…

[论文笔记]LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale

引言 今天带来第一篇量化论文LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale笔记。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 大语言模型已被广泛采用&#xff0c;但推理时需要大量的GPU内…

2024年智能录屏解决方案全攻略,从桌面到云端

如果你有过录屏经验那你一定遇到过被限制录制时长或者录制的画面比较模糊之类的情况。这次我我推荐几款免费录屏软件&#xff0c;让我们可以更自由的录制屏幕画面。 1.福晰REC大师 链接&#xff1a;www.foxitsoftware.cn/REC/ 这款软件便捷好操作&#xff0c;而且符合我这次…

post请求中有[]报400异常

序言 在和前端同学联调的时候&#xff0c;发现只要post请求参数里面有[]&#xff0c;就会报400的错误 可以看到日志中&#xff1a; The valid characters are defined in RFC 7230 and RFC 3986 解决办法&#xff1a; 参考了博客&#xff1a; spring boot 中解决post请求中有…

辽宁汇聚公益慈善力量,绿葆网络助力辽宁绿色生态建设,彰显企业大爱

9月5日&#xff0c;于辽宁省沈阳市隆重举行的“中华慈善日”主题宣传活动暨“山海有情 天辽地宁”即开型福利彩票发行、“生态公益林”项目启动仪式上&#xff0c;广州绿葆网络发展有限公司作为受邀企业之一&#xff0c;积极履行社会责任&#xff0c;向辽宁省慈善联合总会捐赠了…

Linxu系统:kill命令

1、命令详解&#xff1a; kill命令是用于向进程发送信号&#xff0c;通常用来终止某个指定PID服务进程&#xff0c;kill命令可以发送不同的信号给目标进程&#xff0c;来实现不同的操作&#xff0c;如果不指定信号&#xff0c;默认会发送 TERM 信号&#xff08;15&#xff09;&…

[论文笔记]Making Large Language Models A Better Foundation For Dense Retrieval

引言 今天带来北京智源研究院(BAAI)团队带来的一篇关于如何微调LLM变成密集检索器的论文笔记——Making Large Language Models A Better Foundation For Dense Retrieval。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们&quo…

Deploying Spring Boot Apps Tips

Java PaaS providers chatter command Efficient deployments See also spring-boot-reference.pdf https://docs.spring.io/spring-framework/reference/integration/checkpoint-restore.html

【拓扑系列】拓扑排序

【拓扑系列】拓扑排序 前言认识有向无环图认识AOV网&#xff1a;顶点活动图拓扑排序 1. 课程表1.1 题目来源1.2 题目描述1.3 题目解析 2. 课程表 II2.1 题目来源2.2 题目描述2.3 题目解析 3. LCR 114. 火星词典3.1 题目来源3.2 题目描述3.3 题目解析 前言 认识有向无环图 图中…

【局域网投屏】sunshine和moonlight投屏/屏幕共享/扩展屏

主机是sunshine&#xff0c;客机是moonlight&#xff0c;一个太阳一个月光&#xff0c;两者真是太配啦&#xff01; 下载sunshine sunshine是服务器端&#xff0c;去以下GitHub链接下载windows端的解压缩即用版 https://github.com/LizardByte/Sunshine/releases下载完毕解压…