代码编织梦想

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

代码:

<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的打印结果:
在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sqf251877543/article/details/129435217

狂神说Vue笔记整理-爱代码爱编程

狂神说Vue笔记 ​ 想要成为真正的“互联网Java全栈工程师”还有很长的一段路要走,其中前端是绕不开的一门必修课。本阶段课程的主要目的就是带领Java后台程序员认识前端、了解前端、掌握前端,为实现成为“互联网Java全栈工程师”再向前迈进一步。 一、前端核心分析 1.1、概述 Soc原则:关注点分离原则 Vue 的核心库只关注视图层,方便与第三

vue-爱代码爱编程

Vue.js 简介 1.Vue (读音 /vjuː/,类似于 view)的简单认识 (1)Vue是一个渐进式的框架,什么是渐进式的呢? 渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来更丰富的交互体验。或者如果你希望将更多的业务逻辑使用Vue实现,那么Vue的核心库以及其生态系统。比如Core+Vue-router+Vuex,也可以满足你

Vue原理理解与使用-爱代码爱编程

Vue快速上手详解 前言:本文章是本人在学习vue时整理的知识点,初步入门,现在发布在csdn如果有想学习Vue的可以按着步骤来学习,提前告知学习当中Vue需要很多的环境和配置需要安装,请耐心学习。 使用软件:刚开始使用的是IntelliJ IDEA软件,不过使用idea需要下载Vue插件,之后使用的是HBuilder X这个是官方推荐写Vue的软件。

Vue-爱代码爱编程

我的工作台 - Gitee.com 回顾SSM 前端核心分析 Vue概述 Vue:前端体系,前后端分离 Vue.js是一套构建用户界面的渐进式JavaScript框架。与其他重量级框架不同的是,Vue采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文

Vue进阶-爱代码爱编程

Vue 一、MVVM框架概述二、Vue2.1、引入Vue所需的js文件2.2、创建第一个Vue程序2.3、Vue实例对象的常用数据属性【七大属性】2.4、Vue实例对象的常用实例属性2.5、Vue实例对象的常用实例方法2.6、Vue常用指令2.7、Vue条件语句2.8、Vue循环语句2.9、Vue绑定事件210、Vue表单【双向数据绑定】2.11、

Vue学习笔记-爱代码爱编程

记住:核心,数据驱动(虚拟化DOM操作)和组件化的概念 第一部分.是部署项目vuehr项目遇见的问题, 第一个问题: Node Sass does not yet support your current environment: Windows 64-bit with Unsupported runtime (83) For more informa

Vue~~-爱代码爱编程

链接: https://pan.baidu.com/s/1vF2m3nXzjiacwCWCH8r9pg 提取码: c68b ---------错误--------- 安装vue插件 没有Vue Component 安装成功后可以右键看看有没有Vue Component 解决 点击 file 打开设置 settings,展开 Edi

后端掌握Vue知识-爱代码爱编程

Vue框架 想要成为真正的“互联网Java全栈工程师”还有很长的一段路要走,其中前端是绕不开的一门必修课。 1、简介 概述 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还

16.VUE-爱代码爱编程

狂神说 Vue 笔记 Vue 的核心库只关注视图层,方便与第三方库或既有项目整合。 HTML + CSS + JS : 视图 : 给用户看,刷新后台给的数据 网络通信 : axios 页面跳转 : vue-router 状态管理:vuex Vue-UI : ICE , Element UI 一、前端核心分析 1. VUE 概述 Vue (读

前端框架-Vue-爱代码爱编程

Vue Vue 的核心库只关注视图层,方便与第三方库或既有项目整合。 HTML + CSS + JS : 视图 : 给用户看,刷新后台给的数据 网络通信 : axios 页面跳转 : vue-router 状态管理:vuex Vue-UI : ICE , Element UI 1 前端核心分析 1、VUE 概述 Vue (读音/vju/,

vue从入门到精通-爱代码爱编程

VUE vue框架的两大核心: 数据驱动和组件化。 一、前端开发历史 1994年可以看做前端历史的起点(但是没有前端的叫法) 1995年网景推出了JavaScript 1996年微软推出了iframe标签, 实现了异步的局部加载 1999年W3C发布第四代HTML标准,微软推出用于异步数据传输的 ActiveX(ActiveXObject),各大浏

vue学习-爱代码爱编程

VUE 文章目录 VUE@[toc]1、前端核心分析2、前端知识体系2.1 HTML(结构层)2.2 CSS(表现层)2.3JavaScript(行为层)**Typescript 微软的标准**javascrip框架UI框架javascript构建工具2.4三端统一混合开发(Hybrid App)微信小程序2.5后端技术2.6主流前端框架混

vue 笔记_zyhym的博客-爱代码爱编程

Vue 的核心库只关注视图层,方便与第三方库或既有项目整合。 HTML + CSS + JS : 视图 : 给用户看,刷新后台给的数据 网络通信 : axios 页面跳转 : vue-router 状态管理:vuex Vue-UI : ICE , Element UI 一、前端核心分析 1. VUE 概述 Vue (读音/vju/, 类似于

vue2 vue3 js es6 html css 知识点_拾季.的博客-爱代码爱编程

一.生命周期 重点 1.什么是生命周期函数 生命周期函数 又叫钩子函数 是到了某一个时间点会自动触发 我们讨论的是 vue 实例的钩子函数 从 vue 实例创建到销毁的过程中 到了一定的时间节点就会触发 1.1 生命周期函数有哪些 创建阶段 beforeCreate 实例创建之前 这个时候什么都没有 data methods 都不能用 也没

h5 vue-pdf 使用方法 复制粘贴直接用-爱代码爱编程

H5 vue-pdf 使用方法 复制粘贴直接用 <!-- 如果印章不显示 全局搜索这行代码 然后注释掉 重新打包就可以了 _this3.setFlags(_util.AnnotationFlag.HID

笔记:vue 2.0_vue2在vm中读取vc的数据-爱代码爱编程

笔记:Vue 2.0 创建Vue新项目Vue项目文件介绍配置代理 Vue插件Vue实例el 关联元素data 数据methods 函数computed 计算属性watch 侦听(监视)属性filters