点击按钮页面滚动到对应位置(锚点)

前言

回到顶部,相信大家都不陌生吧,这个功能可谓是随处可见,也是作为一名前端开发工程师手到擒来的一个功能点。但前几天刚刚好有一个类似于回到顶部(锚点)的功能点把我卡住了,就是点击锚点按钮页面滚到对应位置。如果说直接显示到对应位置那可真是太简单了,但过程需要点过渡的动画就难到我了,硬生生做了两个小时(说到底还是自己技术不过关)。

需求

点击锚点按钮页面带过渡动画显示到对应位置

HTML

因后期需实现页面滚动到对应位置时锚点高亮,故使用 View Design 框架的步骤条实现锚点按钮样式,为后期功能实现打好基础。

<Steps class="Steps" direction="vertical">
   <Step status="wait" class="stepItem" :key="index" @click.native="changAnchor(item)" v-for="(item, index) in teacherData.groupList" :content="item.groupName"></Step>
</Steps>

失败思路

首次实现时的思路是,如果目前的滚动条高度大于锚点盒子顶部距离页面顶部的高度,便以每10毫秒的速度往上滚(减20),同理若小于则往下滚(加20)。等两则高度相等时则结束此方法(清除计时器),注意 : 一定要清除计时器。(此思路存在问题,好奇的同学可以先思考一下错在哪再往下看)。

失败方法

/**
   * changAnchor 点击切换锚点
   * (this.$refs['scroll'] as any).wrap.scrollTop为本项目自己编写的一个页面滚动条,故此代码不能直接copy复用。
   * 此处代码有些庸余,本应该在计时器前声明一下(this.$refs['scroll'] as any).wrap.scrollTop,但声明之后报了一下莫名其妙的错误,就没有继续往下深究。
  */
  changAnchor(item: any){
    if(this.clickShow === true){
      this.clickShow = false // 用于判断上一次滚动是否执行完毕,如若不执行完毕不执行下一次滚动,否则多次点击动画会出现错乱
      const targetbox: any = document.getElementById(item.firstStepId); // 获取对应锚点盒子
      const target = targetbox.offsetTop - 110;
      this.timeTop = setInterval(() => {
        if((this.$refs['scroll'] as any).wrap.scrollTop > target){
          (this.$refs['scroll'] as any).wrap.scrollTop -= 20
        }
        if((this.$refs['scroll'] as any).wrap.scrollTop < target) {
          (this.$refs['scroll'] as any).wrap.scrollTop += 20
        }
        if((this.$refs['scroll'] as any).wrap.scrollTop === target){
          clearInterval(this.timeTop);
          this.clickShow = true
        }
      }, 1);
    }
  }

失败思路解析

细心的同学可能就会发现这个方法的漏洞,相加相减实现它的动画效果没毛病,但以每次20为间距来相加相减,若此时相加后或者相减后的最后一次距离小于20,那再执行一次相加相减岂不是刚好跳过了它们相等时的值?此时函数会继续往下执行,页面便会一直向上滚或者往下滚,并且因为没有清除计时器边永久执行此方法。

可行思路

继续延用上诉的思路,但在此基础上进行优化。当相加或相减的距离小于20时,将滚动条高度直接赋值成锚点盒子的高度。

可行方法

/**
   * changAnchor 点击切换锚点
  */
  changAnchor(item: any){
    if(this.clickShow === true){
      this.clickShow = false // 用于判断上一次滚动是否执行完毕,如若不执行完毕不执行下一次滚动,否则多次点击动画会出现错乱
      const targetbox: any = document.getElementById(item.firstStepId); // 获取对应锚点盒子
      const target = targetbox.offsetTop - 110;
      this.timeTop = setInterval(() => {
        if((this.$refs['scroll'] as any).wrap.scrollTop > target + 20){
          (this.$refs['scroll'] as any).wrap.scrollTop -= 20
        } else if((this.$refs['scroll'] as any).wrap.scrollTop <= target +20 && (this.$refs['scroll'] as any).wrap.scrollTop >= target){
          (this.$refs['scroll'] as any).wrap.scrollTop = target
        }
        if((this.$refs['scroll'] as any).wrap.scrollTop < target - 20) {
          (this.$refs['scroll'] as any).wrap.scrollTop += 20
        } else if((this.$refs['scroll'] as any).wrap.scrollTop >= target - 20 && (this.$refs['scroll'] as any).wrap.scrollTop <= target){
          (this.$refs['scroll'] as any).wrap.scrollTop = target
        }
        if((this.$refs['scroll'] as any).wrap.scrollTop === target){
          clearInterval(this.timeTop);
          this.clickShow = true
        }
      }, 1);
    }
  }

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

总结

本题的思路其实跟循环差不多,只是单纯的把循环事件变成计时器来使用,所以其他同学若是感兴趣也可以试着使用循环的方式来实现。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

别来…无恙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值