锚点组件--支持点击、滚动高亮锚点

news2024/9/24 13:19:59
实现一个锚点组件,页面滚动时高亮当前位置锚点、点击锚点时跳转到指定冒点位置,同时选中锚点也高亮
效果图

在这里插入图片描述

父组件
import './index.less';
import Anchor from './Anchor';
import Content from './Content';

export default function index() {
  return (
    <div className="wrapper">
      <div className="header"></div>
      <div className="content-wrapper">
        // 锚点组件
        <Anchor />
        // 页面内容组件
        <Content />
      </div>
      <div className="footer"></div>
    </div>
  );
} 
锚点组件
import React, { useState, useEffect, useRef } from 'react';

export default function Anchor() {
  const [currentIndex, setCurrentIndex] = useState(0);
  const isClickRef = useRef<any>(null);
  const timerRef = useRef<any>(null);
  const anchorList = ['anchor-1', 'anchor-2', 'anchor-3', 'anchor-4'];

  function onClickAnchor(item: string, index: number) {
    const el = document.querySelector(`#${item}`) as HTMLElement;
    if (el) {
      window.scroll({
        top: el.offsetTop - 130,
        behavior: 'smooth',
      });
    }
    setCurrentIndex(index);
    // 点击时设置为true,为了防止同时执行滚动事件
    isClickRef.current = true;
    // 清除定时器,防止滚动事件触发、出现走马灯闪烁问题
    if (timerRef.current) clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      isClickRef.current = false;
    }, 1000);
  }

  function handlerScroll() {
    // 点击锚点时,不执行滚动函数
    if (isClickRef.current) return;
    // 获取滚动容器的滚动高度(这里相对于body滚动的)
    const scrollTop =
      document.documentElement.scrollTop || document.body.scrollTop;
    // 获取所有content的item集合
    const contentList = document.querySelectorAll('.content div') as any;
    const offsetTopArr: any = [];
    contentList?.forEach((item: any) => {
      // 获取每个content中item的offsetTop
      offsetTopArr.push(item.offsetTop);
    });
    for (let i = 0; i < offsetTopArr.length; i++) {
      // 当滚动条高度达到对应content中item的滚动高度、则将锚点设置为高亮状态
      if (scrollTop + 120 >= offsetTopArr[i]) {
        setCurrentIndex(i);
      }
    }
  }
  useEffect(() => {
    window.addEventListener('scroll', handlerScroll);
    return () => {
      window.removeEventListener('scroll', handlerScroll);
    };
  }, []);

  return (
    <div className="anchor-wrapper">
      {anchorList?.map((item, index) => (
        <div
          key={item}
          className={`${currentIndex === index && 'active'} item`}
          onClick={() => onClickAnchor(item, index)}
        >
          {item}
        </div>
      ))}
      <div
        className={`slide-bar slide-bar-${currentIndex}`}
        style={{ top: `${currentIndex * 60}px` }}
      ></div>
    </div>
  );
}

页面内容组件
import React from 'react';

export default function Content() {
  const contentList = [
    { id: 'anchor-1', content: 'content-1' },
    { id: 'anchor-2', content: 'content-2' },
    { id: 'anchor-3', content: 'content-3' },
    { id: 'anchor-4', content: 'content-4' },
  ];
  return (
    <div className="content">
      {contentList?.map((item, index) => (
        <div key={index} id={item.id} className="item">
          {item.content}
        </div>
      ))}
    </div>
  );
}

index.less样式文件
.wrapper {
  .header,
  .footer {
    background-color: orange;
  }
  .header {
    // 固定头部
    position: sticky;
    top: 0;
    height: 100px;
  }
  .footer {
    height: 240px;
  }

  .content-wrapper {
    .anchor-wrapper {
      width: 170px;
      float: left;
      position: sticky;
      top: 110px;
      padding: 0 10px;
      border-left: 4px solid #e0e0e0;
      margin: 10px;
      .item {
        padding: 20px 0;
        cursor: pointer;
      }
      .item.active {
        color: #082b6e;
      }

      .slide-bar {
        position: absolute;
        height: 60px;
        width: 4px;
        left: -4px;
        background-color: #082b6e;
        transition: all 0.2s linear;
      }
      .slide-bar-2,
      .slide-bar-3 {
        transform: translateY(6px);
      }
    }

    .content {
      width: calc(100% - 200px);
      margin-left: 200px;
      .item {
        height: 600px;
        font-size: 24px;
        border: 1px solid red;
      }
    }
  }
}

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

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

相关文章

rocketmq的流程

生产过程 消费过程 存储 在RocketMQ中&#xff0c;一个Broker的所有Topic的消息都会被写入到同一个CommitLog文件中。 每个队列&#xff08;Queue&#xff09;都有对应的ConsumeQueue文件。 ConsumeQueue每个记录定长&#xff0c;20字节&#xff0c;消息在commitlog中的偏移量…

【软件的安装与基本设置】AD21软件的PCB规则设置

在绘制PCB之前&#xff0c;要进行规则的创建&#xff0c;因为在绘制PCB的过程中&#xff0c;难免会出现很多错误&#xff0c;所以需要先对绘制PCB创建规则&#xff0c;即所有的打孔&#xff0c;走线&#xff0c;铺铜都要基于电气性能规则去设计&#xff0c;等到后期&#xff0c…

[vue] nvm

nvm ls // 看安装的所有node.js的版本nvm list available // 查显示可以安装的所有node.js的版本可以在可选列表里。选择任意版本安装&#xff0c;比如安装16.15.0 执行&#xff1a; nvm install 16.15.0安装好了之后。可以执行&#xff1a; …

云服务器修改端口通常涉及几个步骤

云服务器修改端口通常涉及几个步骤 远程连接并登录到Linux云服务器&#xff1a; 使用SSH工具&#xff08;如PuTTY、SecureCRT等&#xff09;远程连接到云服务器。 输入云服务器的IP地址、用户名和密码&#xff08;或密钥&#xff09;进行登录。 修改SSH配置文件&#xff1a…

智能数据提取:在严格数据治理与安全标准下的实践路径

一、引言 随着信息技术的飞速发展&#xff0c;数据已成为企业最宝贵的资产之一。然而&#xff0c;数据量的爆炸式增长和数据格式的多样化&#xff0c;使得传统的数据提取方法变得效率低下且难以满足业务需求。智能数据提取技术应运而生&#xff0c;它通过应用人工智能和机器学…

Unity里的Time

Time and frame rate management Time类&#xff1a; Time script reference page. 一些常见的属性有&#xff1a; Time.time 返回从游戏开始经历的时间.Time.deltaTime 返回从上帧结束到现在经历的时间&#xff0c;和帧率成反比Time.timeScale 控制时间流逝的因子Time.fixe…

一个制剂生产人眼中的精益管理

精益管理&#xff08;Lean Management&#xff09;是一种通过减少浪费和提高价值创造的方法&#xff0c;广泛应用于各个行业中&#xff0c;包括药品制剂生产领域。 本文&#xff0c;以一个多年从事药品制剂生产的人的角度&#xff0c;从优点、功能以及与其他管理方法的比较等方…

交通灯-设计说明书

设计摘要&#xff1a; 本设计基于单片机技术&#xff0c;旨在实现智能化交通信号控制&#xff0c;并具备夜间模式、禁止通行模式、同行模式切换以及车流量监测功能。通过按键S1和S2实现夜间模式和禁止通行模式的切换&#xff0c;确保夜间交通安全和禁止通行的需要。按键S3和S4…

阿里云OSS如果指定某个文件夹给子账户

** 第一步创建子账号 ** 创建完用户不要给任何权限&#xff01; 当前页面切换到认证管理获取AccessKey即可 第二步目录授权 找到对应桶文件目录 上面授权按钮操作 选择添加的子账号账号保存即可&#xff01;

springmvc核心流程

核心流程及配置 核心流程 执行流程 用户发送请求到DispatcherServlet前端控制器&#xff0c;前端控制器收到请求后自己不进行处理&#xff0c;而是委托给其他的解析器进行处理&#xff0c;作为统一访问点&#xff0c;进行全局的流程控制 DispatcherServlet调用HandlerMapping映…

电机完美控制的感觉如何【应用案例】

当电机控制技术成为人体的一部分时&#xff0c;对控制系统的组件尺寸和可靠性要求将极大提高。得益于集成式FOC控制系统组件&#xff0c;第一款具有两个活动关节的义肢得以在短时间内完成—— 赶上在苏黎世举办的全球半机械人奥运会(Cybathlon)。 失去肢体显然会对一个人的生活…

社交媒体的探索者:探寻Facebook的发展历程

在当今数字化时代&#xff0c;社交媒体已经成为了人们日常生活中不可或缺的一部分&#xff0c;而Facebook作为最具影响力的社交平台之一&#xff0c;其发展历程承载着无数的探索与创新。本文将深入探讨Facebook的发展历程&#xff0c;从其创立初期到如今的全球化影响&#xff0…

MySQL深入理解MVCC机制(详解)

深入理解MVCC 1、MVCC定义 MVCC:Multi-Version Concurrency Control&#xff0c;多版本并发控制机制。 在mysql中&#xff0c;为了满足事务的四大特性之一的隔离性&#xff0c;就是当前事务中的查询的数据不受其他事务的增删改操作的影响&#xff0c;因此mysql主要是通过这个…

智能鱼缸-设计说明书

设计摘要&#xff1a; 本论文以STC89C52单片机为核心控制器&#xff0c;构建了一套智能鱼缸系统。该系统由中控部分、输入部分和输出部分组成。中控部分采用STC89C52单片机&#xff0c;负责获取输入部分数据并进行处理&#xff0c;控制输出部分。输入部分包括TDS水质水温检测模…

PyCharm2023 社区版安装 +中文语言包+配置教程+Python环境搭建

一、Python 安装 我们在安装Pycharm之前&#xff0c;首先要先安装Python环境也就是安装Python解释器 因为PyCharm是一个用于编写和调试Python代码的开发工具&#xff0c;而Python解释器是用于解释执行Python代码PyCharm需要依赖Python解释器来执行Python代码&#xff0c;因此…

【Stable Diffusion】 训练方法篇

一、四种模型训练方法简介 Stable Diffusion 有四种训练模型的方法&#xff1a;Textual Inversion、Hypernetwork、LoRA 和 Dreambooth 。它们的训练方法存在一定差异&#xff0c;我们可以通过下面对比来评估使用哪种训练方式最适合你的项目。 如果你知道模型中已经可以产生你…

AI图像生成-调整

一、两张图画风不相似 2、在两张图的共同输出口新添加一个空白正面提示词板块和条件合并板块 二、预处理插件&#xff08;提取人物姿态&#xff09; 1、新建节点-》ControlNet预处理器-》面部与姿态-》Openpose姿态预处理器 2、添加上传图片板块与预览图片板块 3、提取姿态 右…

java内容快速回顾+SSM+SpringBoot简要概述

文章目录 java基础知识基本知识列表面对对象堆与栈的关系值修改与引用修改异常&#xff1a;错误异常 SSMspringMVCServletSpringMVC&#xff1a;基于 Servlet的 Spring Web 框架&#xff0c; spring控制反转 IoC(Inversion of Control)面向切面 Aop MybatisJDBCMybatis SpringB…

【3DMAX教程插件】中文3DMax造雪助手造雪覆盖模拟插件使用方法

3DMax造雪助手造雪覆盖模拟插件使用教程 3DMax造雪助手&#xff0c;造雪覆盖模拟插件&#xff0c;只需点击鼠标即可创建逼真的雪。它包括5个自定义对象和6个专门为雪创建工作流设计的修改器。通过各种参数&#xff0c;可以在各种情况下获得所需的效果。 【适用版本】 3dMax201…

班组长不懂如何与下属沟通?教你用“三要四不要”沟通原则

班组长与员工关系和谐程度&#xff0c;对团队士气效率影响重大。 与员工关系融洽的班组长更能获得大家的信任和支持&#xff0c;还会促进团队凝聚力&#xff0c;激发员工的工作积极性和创新精神。 然而&#xff0c;有的班组长只有在安排工作、教导技能时充满自信&#xff0c;…