vue+IntersectionObserver + scrollIntoView 实现电梯导航

news2024/11/19 5:57:11

一、电梯导航

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

二、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.coloradmin.cn/o/2132775.html

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

相关文章

加密软件有哪些数据防护功能?

1.文件透明加密&#xff1a;采用透明加密技术&#xff0c;自动对指定类型的敏感文件进行实时加密&#xff0c;确保数据在存储和传输过程中的安全性。 2.权限管理与访问控制&#xff1a;通过细粒度的权限管理&#xff0c;控制员工对敏感数据的访问权限&#xff0c;包括读取、修…

基于SpringBoot+Vue的预制菜平台系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

蓝桥杯真题——数星星

输入样例&#xff1a; 5 1 1 5 1 7 1 3 3 5 5 输出样例&#xff1a; 1 2 1 1 0 分析&#xff1a; 根据题目&#xff0c;是逐行读入数据&#xff0c;我们要求每颗星星左下方的星星数量&#xff0c;就是要迅速求一个区间内的值 于是我们联想到树状数组来解决问题 代码演示…

商业银行零售业务数智运营探索与应用

一、商业银行零售业务面临新形势 根据国家金融监督管理总局近期发布的数据,2024年一季度商业银行净息差降至1.54%,较2023年四季度的1.69%下降15个基点。在当前经营环境复杂、客户投资预期降低等多重因素的叠加作用下,商业银行经营压力日益加大。与此同时,随着数字化转型的不…

【技术调研】三维(3)-ThreeJs-几何体、材质、贴图、灯光及案例

几何体 ​ 几何体是构建模型的基础,模型=几何体+材质。threejs中已内置了很多几何体。这里不一一介绍。 BufferGeometry 是面片、线或点几何体的有效表述。包括顶点位置,面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值。使用 BufferGeometry 可以有效减少向 GPU 传输…

代码随想录训练营Day3 | 链表理论基础 | 203.移除链表元素 | 707.设计链表 | 206.反转链表

今天任务&#xff1a;学习链表理论基础 链表的类型 链表的存储方式 链表的定义…

基于SpringBoot+Vue+MySQL的招聘管理系统

系统展示 用户前台界面 管理员后台界面 企业后台界面 系统背景 在当今数字化转型的大潮中&#xff0c;企业对于高效、智能化的人力资源管理系统的需求日益增长。招聘作为人力资源管理的首要环节&#xff0c;其效率与效果直接影响到企业的人才储备与竞争力。因此&#xff0c;构建…

linux 操作系统下crontab命令及使用案例介绍

linux 操作系统下crontab命令及使用案例介绍 Linux 操作系统下的 crontab 命令用于设置周期性执行的任务 crontab 命令概述 基本语法 bash crontab [-u user] file crontab [-u user] [-l | -r | -e] [-i] [-s] 主要功能 创建、编辑和管理用户的计划任务&#xff08;cron…

基于中心点的目标检测方法CenterNet—CVPR2019

Anchor Free目标检测算法—CenterNet Objects as Points论文解析 Anchor Free和Anchor Base方法的区别在于是否在检测的过程中生成大量的先验框。CenterNet直接预测物体的中心点的位置坐标。 CenterNet本质上类似于一种关键点的识别。识别的是物体的中心点位置。 有了中心点之…

【工具】前端JavaScript代码在线执行器 方便通过网页 手机测试js代码

【工具】前端JavaScript代码在线执行器 方便通过网页 手机测试js代码 自动补全js代码格式化代码色彩打印日志清空日志待补充 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport"…

基于SpringBoot+Vue+MySQL的热门网络游戏推荐系统

系统展示 用户前台界面 管理员后台界面 系统背景 基于SpringBootVueMySQL的热门网络游戏推荐系统&#xff0c;其背景主要源于当前网络游戏市场的蓬勃发展与用户需求的日益多样化。随着互联网的普及和技术的不断进步&#xff0c;网络游戏已成为人们休闲娱乐的重要方式之一。面对…

JAVA开源项目 校园管理系统 计算机毕业设计

本文项目编号 T 026 &#xff0c;文末自助获取源码 \color{red}{T026&#xff0c;文末自助获取源码} T026&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 管…

网络安全-intigriti-0422-XSS-Challenge Write-up

目录 一、环境 二、解题 2.1看源码 一、环境 Intigriti April Challenge 二、解题 要求&#xff1a;弹出域名就算成功 2.1看源码 我们看到marge方法&#xff0c;肯定是原型链污染题目 接的是传参&#xff0c;我们可控的点在于qs.config和qs.settings&#xff0c;这两个可…

逆向工程 反编译 C# net core

索引器访问 在您的代码中&#xff0c;您试图使用 configurationRoot.get_Item("AgileConfig:appId") 来访问配置项&#xff0c;但这里存在几个问题&#xff1a; 错误的访问方法&#xff1a;在 .NET 的 IConfigurationRoot 接口中&#xff0c;没有直接名为 get_Item 的…

python fastapi 打包exe

创建虚拟环境 python -m venv 国内依赖仓库 # 换源 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple pip config set install.trusted-host mirrors.aliyun.com 安装nuitka pip install nuitka 生成exe nuitka --mingw64 --show-progress --s…

[000-01-008].第08节:Sentinel 环境搭建

1.Sentinel的构成&#xff1a; 核心库-后台默认的端口是8719控制台-前台默认的是8080端口 2.2.搭建Sentinel环境&#xff1a; a.下载Sentinel&#xff1a; 1.sentinel官方提供了UI控制台&#xff0c;方便我们对系统做限流设置。可以在GitHub下载 b.下载后运行Sentinel&#…

自动驾驶系列—掌握速度,驾驭安全,限速信息提醒功能(SLIF)介绍

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

心觉:收钱就像喝水一样简单,是如何做到的?

Hi&#xff0c;我是心觉&#xff0c;与你一起玩转潜意识、脑波音乐和吸引力法则&#xff0c;轻松掌控自己的人生&#xff01; 挑战每日一省写作168/1000天 真的存在赚钱跟喝水一样简单的事情 这两天亲身体验过 某位做知识付费的大佬&#xff0c;昨天写一篇文章就哗哗哗的开…

导弹追踪问题:蒙特卡罗模拟+matlab代码

问题描述 蒙特卡罗模拟思想 采用微元法以直代曲的思想&#xff0c;假设一个个小时间段内&#xff0c;B船先走完一段直线距离后&#xff0c;导弹朝着两者连线方向走直线&#xff0c;这样若干条直线便近似拟合导弹轨迹。代码中判断碰撞的依据是A、B之间的距离小于某个阈值&#x…

JSON 数据 Excel 行转列

有如下JSON数据 [{id:1,name:小明,score:90}, {id:2,name:小李,score:89}, {id:3,name:小王,score:77}, {id:4,name:小刘,score:56}] 粘贴到 Excel 选中列-->数据tab-->分列 下一步 分隔符号-->其他【,】-->完成 CtrlF 替换-->全部替换 掉不要的字符为空 得…