Vue项目实现滚动条顺畅滑动及对之前锚点监听的优化(Pc端)

首先看一下效果 ,返回顶点效果:

这里其实就是封装了一个scroll.js

  • 过渡用的是requestAnimationFrame,这货只支持IE10+,所以必须做兼容
  • 滚动视图是window.pageYOffset,这货支持IE9+;

这里只是转载了他人写好封装完毕的代码,原文链接:https://blog.csdn.net/crper/article/details/76038932?utm_source=tuicool&utm_medium=referral

现在讲一下之前做的锚点平滑滚动:

首先我拿到这一块代码的时候,看了一下源码,发现好像不适合我的锚点

因为这代码的是拿取浏览器上滚动条距离顶端的距离,就是document对象,但是我这边完全用不了,我的只是父子级div滚动,但是看了代码之后,我可以将scroll.js提取出来,修改里面的源码,将其当做一个vue methods中的一个普通方法来做,优化之前一小段一小段的跳来实现假平滑移动的效果

情况和我之前的https://mp.csdn.net/postedit/80397826 这篇布局是一模一的,只是子组件页面(days-detail.vue)实现(代码和之前的一样,我这边简单放几个关键代码)

   <el-steps
      direction="vertical"
      space="2.5rem"
      finish-status="wait"
      :active="activeStep">
      <el-step
        v-for="(item, index) in steps"
        :key="index"
        icon=" "
        :title="item.title"
        @click.native="jump(index)">
        <dl-icon slot="icon" :type="index === 0 ?  'yinzhang' : 'shixinyuan'"
                 :color="index === 0 ?  '#383636' : '#BEC0C2'" size="0.5"></dl-icon>
      </el-step>
    </el-steps>
    //  js  跳转
       jump (index) {
        // 用 class="step-jump" 添加锚点
        let jump = document.querySelectorAll('.step-jump')
        let total = jump[index].offsetTop
        this.currentIndex = index
        this.$emit('viewScroll', total, index)
      },
  
  // 监听滚动条滚动
     props: ['scrollTop'],
    watch: {
      scrollTop (val) {
        this.onScroll()
        this.pageTop = val
      }
    },
   onScroll (index) {
        let _article = document.querySelectorAll('.step-jump')
        let arr = []
        _article.forEach((item, index) => {
          if (this.scrollTop >= item.offsetTop) {
            this.activeStep = index
            arr.push(index)
          }
        })
        let maxIndex = Math.max.apply(null, arr)
        if (index !== undefined && index > maxIndex) {
          this.activeStep = index
        }
      },

其实可以发现和之前的初版跳转修改新增了几行代码,首先jump函数,用一个currentIndex 来记录点击的锚点,并携带这传递到父组件中去,onscroll函数中,新增判断点击的锚点是不是大于滚动条最大的高度所经过的最大锚点,两者合起来就是为了解决,最下面的几个锚点的offsettop有可能大于滚功条的最大滚动距离,导致,点击的锚点(也就是step节点)并不能渲染成点击状态(我这边是变红),当然,这里是要区分点击滚动,还是滚动条滚动,so,下面会说一下。

在父组件(layout.vue)中

    <el-col :span="24" class="layout__cont">
      <sub-menu :currentMenu="currentMenu"></sub-menu>
      <section class="layout__content" @scroll="handleScroll" ref="content">
        <div :span="24" class="content-wrapper">
          <transition name="fade" mode="out-in" style="min-height: 1000px">
            <router-view :scrollTop="scrollTop" @viewScroll="viewScroll" ref="scrollTo"></router-view>
          </transition>
        </div>
        <dl-footer></dl-footer>
      </section>
    </el-col>
  // js
  handleScroll (el) {
        this.scrollTop = this.$refs.content.scrollTop
      },
      viewScroll (val, index) {
        this.clickIndex = index
        this.scrollIt(val, 500, 'linear')
      },

可以看到父组件基本没什么变化,除了多了一个 接受子组件传过来的index值进行保存起来,router-view组件上新增ref属性来调用子组件的方法。

最后最主要的是 scroll.js代码的改变,其实就是改了其中几个计算滚动条高度

 scrollIt (destination = 0,
                duration = 200,
                easing = 'linear',
                callback) {
        let that = this
        // define timing functions -- 过渡动效
        let easings = {
          // no easing, no acceleration
          linear (t) {
            return t
          },
          // accelerating from zero velocity
          easeInQuad (t) {
            return t * t
          },
          // decelerating to zero velocity
          easeOutQuad (t) {
            return t * (2 - t)
          },
          // acceleration until halfway, then deceleration
          easeInOutQuad (t) {
            return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
          },
          // accelerating from zero velocity
          easeInCubic (t) {
            return t * t * t
          },
          // decelerating to zero velocity
          easeOutCubic (t) {
            return --t * t * t + 1
          },
          // acceleration until halfway, then deceleration
          easeInOutCubic (t) {
            return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
          },
          // accelerating from zero velocity
          easeInQuart (t) {
            return t * t * t * t
          },
          // decelerating to zero velocity
          easeOutQuart (t) {
            return 1 - --t * t * t * t
          },
          // acceleration until halfway, then deceleration
          easeInOutQuart (t) {
            return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t
          },
          // accelerating from zero velocity
          easeInQuint (t) {
            return t * t * t * t * t
          },
          // decelerating to zero velocity
          easeOutQuint (t) {
            return 1 + --t * t * t * t * t
          },
          // acceleration until halfway, then deceleration
          easeInOutQuint (t) {
            return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t
          }
        };
        // requestAnimationFrame()的兼容性封装:先判断是否原生支持各种带前缀的
        // 不行的话就采用延时的方案
        (function () {
          var lastTime = 0
          var vendors = ['ms', 'moz', 'webkit', 'o']
          for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
            window.requestAnimationFrame =
              window[vendors[x] + 'RequestAnimationFrame']
            window.cancelAnimationFrame =
              window[vendors[x] + 'CancelAnimationFrame'] ||
              window[vendors[x] + 'CancelRequestAnimationFrame']
          }

          if (!window.requestAnimationFrame)
            window.requestAnimationFrame = function (callback, element) {
              var currTime = new Date().getTime()
              var timeToCall = Math.max(0, 16 - (currTime - lastTime))
              var id = window.setTimeout(function () {
                callback(currTime + timeToCall)
              }, timeToCall)
              lastTime = currTime + timeToCall
              return id
            }

          if (!window.cancelAnimationFrame)
            window.cancelAnimationFrame = function (id) {
              clearTimeout(id)
            }
        })()

        // let element = checkElement()
        // let start = element.scrollTop // 当前滚动距离
        let start = this.scrollTop
        let startTime = Date.now() // 当前时间

        function scroll () { // 滚动的实现
          let now = Date.now()
          let time = Math.min(1, (now - startTime) / duration)
          let timeFunction = easings[easing](time)
// ==================================================
          that.$refs.content.scrollTop = timeFunction * (destination - start) + start
         start = timeFunction * (destination - start) + start
          if (start === destination) {
            that.$refs.scrollTo.onScroll(that.clickIndex)
            callback // 此次执行回调函数
            return
          }
// ==================================================
        scroll()
      }

这边可以对比别人封装的,可以看到 除了将scrolltop改成自己的属性外,你会发现timeFunction * (destination - start) + start 这变量 我赋给了两个值,这完全是不一样的,that.$refs.content.scrollTop 这是为了实现滑动效果的功能,而start则是记录是否已经滑动结束,因为that.$refs.content.scrollTop 这代表着滚动条的最大高度,是有可能 = 并不能成立,so,用start代替,(这个坑我用了很多种方法来实现,最后才恍然大悟!!!!!)来告诉代码,可以callback 结束滑动了,我在这里面还加了that.$refs.scrollTo.onScroll(that.clickIndex) 来去告诉子组件去渲染 点击的step 和告诉子组件onScroll里的形参index 来区别是滚动还是点击滚动(我这个也想了超级久,突然的思路)。。。。so 随便写了点上面的笔记。。。最后实现的效果相比于之前有很大进步!!!

其实别看实现的代码其实hen简单,但是里面的坑不做不知道,在凌晨12.30解决这个问题,还是很幸福的!代码的魅力?! 哈哈!

放一下效果图

猜你喜欢

转载自blog.csdn.net/shentibeitaokong/article/details/81349819