vue锚点双向绑定

  • 需求描述:

头部固定吸顶,右侧菜单,左侧长页面,要求左侧滚动右侧菜单对应高亮,点击右侧菜单,左侧页面滚动到对应位置。(这个对应位置就是模块头部刚好在固定头部下面)

  • 需求分析:

头部和右侧可使用fixed定位,左侧长页面不限制高度。有两件事需要处理:监听滚动事件,实现滚动效果。

  • 解决问题
// 监听事件添加和销毁
mounted() {
    document.addEventListener('scroll', this.handleScroll)
},
    
beforeDestroy() {
    document.removeEventListener('scroll', this.handleScroll)
}

// 滚动事件
handleScroll() {
    // 滚动高度
  const sTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
  // 最大值,当页面滚动到最大值后,下面的模块不会再向上滚动了
  const max = this.sArea[8] + 10
  
  // 滚动停止后执行函数
  runLastFn(_ => {
    this.initSArea()
    if (sTop < max) {
      // 判读页面滚动对应的菜单index
      const idx = this.findAreaIndex(sTop)
      if (!this.mFlag) return
      this.currentFloor = 'block' + idx
    }
  })
},
    
// 初始化不同模块(12个)滚动间距,注意每个模块都设置一个ref,每个模块滚动的距离组成一个数组,与菜单对应
initSArea() {
  const arr = []
  for (let i = 1; i < 12; i++) {
    const height = this.$refs[`block${i}`].offsetTop - 240 // 240 是所有模块滚动目标位置
    arr.push(height > 0 ? height : 0)
  }
  this.sArea = arr
  this.boxScreenHeight = this.$refs.boxRef.scrollHeight
},
    
// 滚动距离在数组中位置,以此判读哪个菜单应该在选择状态
findAreaIndex(val) {
  const sArea = this.sArea
  const len = sArea.length
  if (val < sArea[1]) return 1
  if (val > sArea[len - 1]) return len - 1

  let idx = 0
  let i = 0
  for (i; i < len; i++) {
    if (sArea[i] > val) {
      idx = i
      break
    }
  }
  return idx
}

// 点击菜单滚动到对应模块
gotoBlock(el, speed) {
  const idx = Number(el.slice(5))
  const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop // 滚动条距离顶部高度
  let currentTop = scrollTop // 默认滚动位置为当前滚动条位置,若改为0,则每次都会从顶部滚动到指定位置
  let top = 0 // 需要滚动到的位置

  if (idx < 9) {
    this.mFlag = true
    const i = idx - 1 > 0 ? idx - 1 : 0 // 有个回到顶部菜单,所以第一个和第二个滚动位置一样
    top = this.sArea[i] + 1 // 模块需要滚动的位置
  } else {
    this.mFlag = false // 页面无需滚动了,但是菜单被点击后仍要选中状态
    top = this.boxScreenHeight
    this.currentFloor = el
    setTimeout(_ => {
      this.mFlag = true
    }, 1200)
  }
    
  let requestId
  function step() {
    // 如果需要滚动的位置和当前位置之差小于步长speed,直接滚动到目标位置,否则按照步长滚动
    if (Math.abs(currentTop - top) < speed) {
      window.scrollTo(0, top)
    } else {
      if (scrollTop < top) {
        // 如果滚动条的高度小于元素距离顶部的高度
        if (currentTop <= top) {
          window.scrollTo(0, currentTop)
          requestId = window.requestAnimationFrame(step)
        } else {
          window.cancelAnimationFrame(requestId)
        }
        // 向下滚动
        currentTop += speed
      } else {
        if (top <= currentTop) {
          window.scrollTo(0, currentTop - speed)
          requestId = window.requestAnimationFrame(step)
        } else {
          window.cancelAnimationFrame(requestId)
        }
        // 向上滚动
        currentTop -= speed
      }
    }
  }
  window.requestAnimationFrame(step)
},
原文地址:https://www.cnblogs.com/codebook/p/15380056.html