vue上实现左右关联滚动

news2024/11/28 10:41:55

先看效果:
在这里插入图片描述

代码:

<template>
  <div class="container">
    <!-- 左侧fixed导航区域 -->
    <div class="left">
      <div
        v-for="item in leftList"
        :key="item.id"
        class="left_item"
        :class="item.id == selectedId ? 'selected' : ''"
        @click="leftItemClick(item.id)"
      >
        {{ item.title }}
      </div>
    </div>

    <!-- 右侧内容区域 -->
    <div class="right">
      <div v-for="item in rightList" :key="item.id" class="right_item">
        {{ item.content }}
      </div>
    </div>
  </div>
</template>

<script>
import scrollUtil from "@/common/js/scrollUtil.js";

export default {
  data() {
    return {
      leftList: [],
      rightList: [],
      selectedId: 1,
      offsetTopArr: [],
    };
  },
  created() {
    this.initData();
  },
  mounted() {
    // 监听滚动事件
    window.addEventListener("scroll", this.onScroll);

    this.getOffsetTopArr();
  },
  destroyed() {
    // 移除监听器
    window.removeEventListener("scroll", this.onScroll);
  },
  methods: {
    //获取右侧所有item的offsetTop
    getOffsetTopArr() {
      this.$nextTick(() => {
        let rightListItems = document.querySelectorAll(".right .right_item");
        if (rightListItems && rightListItems.length > 0) {
          rightListItems.forEach((item) => {
            this.offsetTopArr.push(item.offsetTop);
          });
          console.log(this.offsetTopArr);
        }
      });
    },
    //页面滚动监听
    onScroll() {
      // 获取当前文档流的 scrollTop
      const scrollTop =
        document.documentElement.scrollTop || document.body.scrollTop;
      console.log("scrollTop=" + scrollTop);
      for (let i = 0; i < this.offsetTopArr.length; i++) {
        if (scrollTop >= this.offsetTopArr[i]) {
          this.selectedId = this.rightList[i].parentId;
        }
        // todo 优化 => 改成小于等于,加break 少循环
      }
    },
    //左侧item点击事件
    leftItemClick(id) {
      this.selectedId = id;

      let index = 0;
      for (let i = 0; i < this.rightList.length; i++) {
        if (this.rightList[i].parentId == id) {
          index = i;
          break;
        }
      }

      const targetOffsetTop = this.offsetTopArr[index];
      scrollUtil.scrollTo(targetOffsetTop);
    },
    //初始化数据源
    initData() {
      for (let index = 1; index < 10; index++) {
        for (let i = 1; i < 5; i++) {
          this.rightList.push({
            id: index + "-" + i,
            parentId: index,
            content: "content-" + index,
          });
        }
        this.leftList.push({
          id: index,
          title: "title-" + index,
        });
      }
    },
  },
};
</script>

<style lang="less" scoped>
.container {
  position: relative;
  min-height: 100vh;
  background: #fff;

  .left {
    position: fixed;
    width: 120px;
    height: 100%;
    min-height: 100vh;
    overflow: auto;
    float: left;
    background: #f2f2f2;

    .left_item {
      width: 100%;
      height: 60px;
      text-align: center;
      line-height: 60px;
    }

    .selected {
      background: #fff;
      font-weight: bold;
      color: #07c160;
    }
  }

  .right {
    margin-left: 120px;
    width: calc(100vw - 120px);
    overflow: auto;

    .right_item {
      width: 100%;
      height: 200px;
      text-align: center;
      line-height: 200px;
      font-size: 24px;
      border-bottom: 1px solid #ccc;
    }
  }
}
</style>

其中scrollUtil.js:

const scrollUtil = {
  /**
   * 滚动到目标offsetTop
   * @param {*} targetOffsetTop 目标offsetTop
   */
  scrollTo: function (targetOffsetTop) {
    // 当前offsetTop
    let scrollTop =
      document.documentElement.scrollTop || document.body.scrollTop;

    // 定义一次跳50个像素,数字越大跳得越快,但是会有掉帧得感觉
    const STEP = 50;

    // 判断是往下滑还是往上滑
    if (scrollTop > targetOffsetTop) {
      // 往上滑
      smoothUp();
    } else {
      // 往下滑
      smoothDown();
    }

    // 定义往下滑函数
    function smoothDown() {
      // 如果当前 scrollTop 小于 targetOffsetTop 说明视口还没滑到指定位置
      if (scrollTop < targetOffsetTop) {
        // 如果和目标相差距离大于等于 STEP 就跳 STEP
        // 否则直接跳到目标点,目标是为了防止跳过了。
        if (targetOffsetTop - scrollTop >= STEP) {
          scrollTop += STEP;
        } else {
          scrollTop = targetOffsetTop;
        }
        document.body.scrollTop = scrollTop;
        document.documentElement.scrollTop = scrollTop;
        // 屏幕在绘制下一帧时会回调传给 requestAnimationFrame 的函数
        // 关于 requestAnimationFrame 可以自己查一下,在这种场景下,相比 setInterval 性价比更高
        requestAnimationFrame(smoothDown);
      }
    }

    // 定义往上滑函数
    function smoothUp() {
      if (scrollTop > targetOffsetTop) {
        if (scrollTop - targetOffsetTop >= STEP) {
          scrollTop -= STEP;
        } else {
          scrollTop = targetOffsetTop;
        }
        document.body.scrollTop = scrollTop;
        document.documentElement.scrollTop = scrollTop;
        requestAnimationFrame(smoothUp);
      }
    }
  },
};

export default scrollUtil;


注意:样式要加box-sizing配置,消除border的影响,否则左边index根据页面滚动定位会不准确
在这里插入图片描述

没加box-sizing,offsetTopArr的打印结果:
在这里插入图片描述

加了box-sizing,offsetTopArr的打印结果:
在这里插入图片描述

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

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

相关文章

Angular学习之ControlValueAccessor接口详解

ControlValueAccessor 是什么&#xff1f;为什么需要使用 &#xff1f;下面本篇文章就来带大家了解Angular中的ControlValueAccessor组件接口&#xff0c;希望对大家有所帮助&#xff01; ControlValueAccessor 是什么&#xff1f; 简单来说ControlValueAccessor是一个接口&am…

【Linux 网络编程2】应用层协议--http;序列化和反序列化,get和post请求传参的区别,cookie和sesion,编写一个简单的http

目录 1.序列化和反序列化 2.HTTP协议 3.编写一个简单的http 3.2.简单的http的使用 3.3.get和post请求传参的区别 4.http的状态码分类 5.cookie和sesion 1.序列化和反序列化 1.1.序列化和反序列化的优势 序列化将结构体转化为长字符串&#xff0c;便于传输&#xff1b;反序…

MyBatis源码用了哪些设计模式?

MyBatis源码用了哪些设计模式&#xff1f;前言一、创建型模式工厂模式单例模式建造者模式二、结构型模式适配器模式代理模式组合模式装饰器模式三、行为型模式模板模式策略模式迭代器模式总结前言 在 MyBatis 的两万多行的框架源码中&#xff0c;使用了大量的设计模式对工程架…

Oracle OCP 19c 考试(1Z0-083)中关于Oracle不完全恢复的考点(文末附录像)

欢迎试看博主的专著《MySQL 8.0运维与优化》 下面是Oracle 19c OCP考试&#xff08;1Z0-083&#xff09;中关于Oracle不完全恢复的题目: A database is configured in ARCHIVELOG mode A full RMAN backup exists but no control file backup to trace has been taken A media…

Spatial-Temporal Graph ODE Networks for Traffic Flow Forecasting

Spatial-Temporal Graph ODE Networks for Traffic Flow Forecasting 摘要 交通流量的复杂性和长范围时空相关性是难点 经典现存的工作&#xff1a; 1.利用浅图神经网络&#xff08;shallow graph convolution networks&#xff09;和 时间提取模块去分别建模空间和时间依赖…

【Python3安装部署的保姆级教程】

如何在Windows 10上安装Python Python是一种越来越受欢迎的编程语言,无论是对于初学者还是有经验的开发者。Python灵活多用,擅长脚本、自动化、数据分析、机器学习和后端开发。在本教程中,你将学习如何使用Windows的Python安装程序在Windows 10上安装Python。 第一步 — 下…

Python3-错误和异常

Python3 错误和异常 作为 Python 初学者&#xff0c;在刚学习 Python 编程时&#xff0c;经常会看到一些报错信息&#xff0c;在前面我们没有提及&#xff0c;这章节我们会专门介绍。 Python 有两种错误很容易辨认&#xff1a;语法错误和异常。 Python assert&#xff08;断…

进程间通信IPC

进程间通信IPC (InterProcess Communication) 一、进程间通信的概念 每个进程各自有不同的用户地址空间&#xff0c;任何一个进程的全局变量在另一个进程中都看不到&#xff0c;所以进程之间要交换数据必须通过内核&#xff0c;在内核中开辟一块缓冲区&#xff0c;进程1把数据…

MySQL事务详解与隔离级别的实现

文章目录一、四个特性二、存在问题三、隔离级别四、实现原理0、SQL语句执行流程1&#xff09;buffer pool2&#xff09;执行流程1、日志1&#xff09;binlog2&#xff09;redolog3&#xff09;对比4&#xff09;undolog2、MVCC原理1&#xff09;隐式字段2&#xff09;undo log版…

气泡式水位计的安装方法详解

气泡水位计的安装实际上就是气管的安装&#xff0c;气管的安装是否正确将直接影响到仪器测量数据的结果&#xff0c;气泡水位计它由活塞泵产生的压缩空气流经测量管和气泡室&#xff0c;进入被测的水体中&#xff0c;测量管中的静压力与气泡室上的水位高度成正比。那么接下来就…

蓝桥杯集训·每日一题Week1

前缀和&#xff08;Monday&#xff09; AcWing 3956. 截断数组&#xff08;每日一题&#xff09; 思路&#xff1a; 首先可以预处理出前缀和。判断数组长度如果小于 333 或者前 nnn 项不是 333 的倍数&#xff0c;则可以直接输出 000。 否则就枚举所有 s[i]s[n]3s[i] \cfrac…

kali双网卡

先单独开启一个网卡&#xff0c;配置/etc/network/interfaces 修改为如下配置 This file describes the network interfaces available on your system and how to activate them. For more information, see interfaces(5). source /etc/network/interfaces.d/* The loopb…

JVM系列——Java与线程,介绍线程原理和操作系统的关系

并发不一定要依赖多线程(如PHP中很常见的多进程并发)。 但是在Java里面谈论并发&#xff0c;基本上都与线程脱不开关系。因此我们讲一下从Java线程在虚拟机中的实现。 线程的实现 线程是比进程更轻量级的调度执行单位。 线程的引入&#xff0c;可以把一个进程的资源分配和执行调…

【深度强化学习】(3) Policy Gradients 模型解析,附Pytorch完整代码

大家好&#xff0c;今天和各位分享一下基于策略的深度强化学习方法&#xff0c;策略梯度法是对策略进行建模&#xff0c;然后通过梯度上升更新策略网络的参数。我们使用了 OpenAI 的 gym 库&#xff0c;基于策略梯度法完成了一个小游戏。完整代码可以从我的 GitHub 中获得&…

原型模式(设计模式详解)

原型模式 描述 原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;它允许通过复制现有对象来创建新对象&#xff0c;而无需从头开始编写代码。 在原型模式中&#xff0c;一个原型对象作为模板&#xff0c;新的对象通过克隆这个原型对象而创…

MySQL OCP888题解048-letter N in slow query log(慢查询日志里的字母N)

文章目录1、原题1.1、英文原题1.2、中文翻译1.3、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3.1、知识点1&#xff1a;mysqldumpslow - 总结缓慢的查询日志文件4、实验4.1、实验14.1.1、实验目的4.1.2、实验前准备4.1.3、实验步骤4.1.4、实验结论5、总结1、原题 1.1…

dva01-初识

背景 React 本身只是一个 DOM 的抽象层&#xff0c;使用组件构建虚拟 DOM。如果开发大应用&#xff0c;还需要解决一个问题。 通信&#xff1a;React 只提供了一种传参手段&#xff0c;后续数据变化非常麻烦&#xff0c;无法适用于大应用。数据流&#xff1a;每次要更新数据&…

ACE C++网络通信框架深入解析ACE_Message_Block消息类

前言 我所见到最好消息包的接口设计莫过于ACE_Message_Block了。 为什么这样说呢&#xff1f; 对于它的API说明&#xff0c;我最初仅想在它的基础上提供注释说明&#xff0c;而不想多言其它&#xff0c;因为无需多言其他。 不过&#xff0c;后来还是补充两个图&#xff0c;以…

后台搭建常用方式及常用的插件

利用脚手架创建项目 create-vite 是一个快速生成主流框架基础模板的工具,安装后启动预设创建项目 使用NPM&#xff1a; npm create vitelatest 使用Yarn: yarn create vite 使用PNPM: pnpm create vite 开始 | Vite 官方中文文档 create-vue&#xff0c;是 Vue 官方的项目脚…

Flume工作原理 安装配置

目录 简介 主要功能 日志收集 数据处理 工作原理 Flume架构 安装 拷贝压缩包 解压 改名 修改配置文件 安装nc&#xff08;netcat&#xff09; 安装telnet协议 应用 应用一&#xff1a;实时监听 新建netcat-logger.conf文件 开启端口监听方式一 访问主机 开启…