1. 程式人生 > 其它 >vue錨點雙向繫結

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)
},