前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >导航栏滚动吸顶并自动高亮和点击跳转锚点

导航栏滚动吸顶并自动高亮和点击跳转锚点

作者头像
OECOM
发布2021-01-20 10:06:01
10.2K0
发布2021-01-20 10:06:01
举报
文章被收录于专栏:OECOMOECOM

2021-01-16 07:37:33

在阿里云的云市场页面上有一个效果,就是api导航栏当滚动条滚动到其所在位置时,自动吸顶,当滚动到下方所在导航栏指定的介绍时,自动高亮其导航栏。点击时则会滑动至其内容所在位置。具体效果为下图样式。

导航栏滚动吸顶并自动高亮和点击跳转锚点
导航栏滚动吸顶并自动高亮和点击跳转锚点

实现方法

正常情况下我们点击自动定位到其所在位置一般用id锚点的方式,但是这种方式有一个缺陷就是无法实现滚动条缓动效果,而且带url上还会通过hash的方式显示出ID,另外也无法实现滚动到内容所在位置自动高亮导航栏。

那么需要我们自己手动来实现以下,具体实现思路就是增加滚动条监听事件,当滚动到导航栏指定内容区域时,给其导航栏增加高亮样式,点击导航栏时,计算好滚动条的滚动距离,让其滚动过去即可。

代码实现

话不多说,我们直接来实现即可。

我这次采用的是react来写,具体思路都是相同的,无论你用的是vue还是angular 还是使用jq还是原生js,都是一样的。

首先要构建一个导航栏的数据结构,假设导航栏结构是这样的:

代码语言:javascript
复制
let navInfo = [
  {
    name:"产品说明",
    id:"introduce",
    content:"这是产品说明"
  },
  {
    name:"使用指南",
    id:"useFun",
    content:"巴拉巴拉这是使用指南"
  },
  {
    name:"售后服务",
    id:"service",
    content:"巴拉巴拉这是售后服务"
  },
  {
    name:"产品参数",
    id:"proCanshu",
    content:"巴拉巴拉这是产品参数"
  }
]

我们假设导航栏有四个导航,我们将这四个导航和内容渲染到页面上:

代码语言:javascript
复制
  function NavDemo(props){
   const nav_content = useRef();//标识nav导航栏渲染内容
    const [navList,setNavList] = useState(navInfo);//这里使用自行构建的导航栏
    const [fixNav,setFixNav] = useState(false);//用户标识什么时候导航栏吸顶
    const [activeNav,setActiveNav] = useState("");//与标识导航栏高亮

    return <div>
        <div className={"nav_list "+ (fixNav?"active":"") }>
            <ul>
                {navList.map(item=>{
                    return  <li key={item.id}>
                        <a className={activeNav==item.id?"active":""}>{item.name}</a>
                    </li>
                })}
            </ul>
        </div>
        {fixNav && <div className="zhanfIx" />}
        <div ref={nav_content}>
           {navList.map(item=>{
                        {/*这里给ID加key字符串后缀是为了防止页面其他地方的ID重复*/}
               return  <div className="type_group" id={item.id+"_key"} key={item.id+item.name}>
                   <div className="type_title">{item.name}</div>
                   <div>{item.content}</div>
               </div>}
        </div>
    </div>
  }

好了,至此我们已经将内容和导航栏渲染好了,并且给内容部分增加ref,便于后续获取其内容,导航栏也增加何时吸顶的标识以及导航栏高亮的标识,另外增加了一个class为zhanfIx的地址,因为当导航栏吸顶时,此处会因为空出位置,下面内容上移,而产生不和谐的效果,我们需要在其吸顶的同时增加一个div来占位,以增加平滑的效果。

下面我们来看一下导航栏吸顶和滑动到指定位置导航栏高亮的逻辑。

代码语言:javascript
复制
useEffect(()=>{
        //增加滚动条监听事件
        document.addEventListener('scroll', scrollEventListener)
        return ()=>{
            //组件注销时去除监听事件
            document.removeEventListener('scroll', scrollEventListener)
        }
    },[]);
let scrollEventListener = ()=>{
        //获取导航栏显示内容区域信息
        let nav_contentReact = nav_content.current.getBoundingClientRect();
        //获取导航栏显示内容区域直接子元素
        let groupList = Array.from(nav_content.current.children);
        if(nav_contentReact){
            groupList.map(item=>{
                let itemReact = item.getBoundingClientRect();
                if(itemReact.y<=60 && (itemReact.y+itemReact.height) >60){
                  //当该子元素距离顶部小于等于60时,说明此时导航栏应该高亮,
                  //同时在其高度范围内均应高亮。
                    setActiveNav(item.id+"_key")
                }
            })
            //我们设定导航栏的高度是60px,导航栏占位高度同样是60px
            if (nav_contentReact.y <= 60 ) { // 导航-吸顶
                setFixNav(true);
            } else if(nav_contentReact.y > 60 ){
                setFixNav(false);
                //当脱离其显示范围时,导航栏无需高亮
                setActiveNav("")
            }
        }
    }

方法和注释上面写的很清楚,并不是很复杂,原理就是通过ID找到当前视野内的内容属于哪个导航栏,便让其高亮即可。这样我们就实现了通过滚动条来控制导航栏高亮的效果了,接下了我们要实现的便是点击导航栏自动定位到其所在内容。

首先要做的一件事就是给导航栏增加一个点击事件

代码语言:javascript
复制
 <a className={activeNav==item.id?"active":""}
    onClick={()=>navClick(item.id)} >{item.name}</a>

下面来看具体的实现逻辑

代码语言:javascript
复制
    //先定义两个变量
/*上一次滚动条距顶部位置,此变量是为了防止底部高度不够时,
无法定位到最下方,结果导致程序无限循环的bug,
通过下面的代码应该可以明白此变量的意义*/
    let prevScrollTop = null;
    let isToTop = false;//点击锚点时滚动条是向上还是向下

    //导航栏点击事件
    function navClick(id){
        let groupList = Array.from(nav_content.current.children);
        let selectItem = null;
        //循环遍历,查找当前点击的是哪个导航,通过ID确定内容区域
        groupList.map(item=>{
            if(item.id==id){
                selectItem = item
            }
        })
        //获取所选导航指定内容区域位置信息
        let outerItemReact = selectItem.getBoundingClientRect();
        //判断导航内容是在可视区域上方还是下方,来决定滚动条是应该向上滚动还是向下滚动
        isToTop = outerItemReact.y > 60;
        //增加定时循环任务,控制速度逐渐变慢的效果来滚动滚动条。
        const createTimer = setInterval(() => {
            let itemReact = selectItem.getBoundingClientRect();
            const top = document.documentElement.scrollTop || document.body.scrollTop;
            //计算滚动速度及方向
            let ispeed = itemReact.y < 60 ? -(Math.abs(itemReact.y)/8):(Math.abs(itemReact.y)/8)
            //防止速度出现过慢情况,则指定最小速度
            if(Math.abs(ispeed)<60/8){
                ispeed = ispeed > 0 ? 60/8: -60/8;
            }
            if ( (isToTop && itemReact.y < 60) || (!isToTop && itemReact.y > 60)  || prevScrollTop == 60) {
                clearInterval(createTimer);
                prevScrollTop = null;
                document.documentElement.scrollTop = document.body.scrollTop = top + (itemReact.y - 60)
            } else {
                prevScrollTop = top;
                document.documentElement.scrollTop = document.body.scrollTop = top + ispeed
            }
        }, 30)
    }

整个功能到此就已经完全实现好了,实现了滚动条滚动时自动高亮导航栏,并超过导航栏位置自动吸顶效果,同时点击导航栏滚动条缓动至锚点位置,实现的最终效果可以看阿里云市场详情页中的效果,比他显示的效果多了滚动条缓动效果。上述的例子我没有贴出css样式,具体实现请大家自己动手写写即可。

如有bug欢迎大家指正。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实现方法
  • 代码实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档