Vue實現左右菜單聯動實現

知乎html

我的博客vue

Githubgit

源碼傳送門:Rain120/vue-studygithub

根據掘金評論需求,更新了數據接口並修復了一些問題

以前在外賣軟件上看到這個左右聯動的效果,以爲頗有意思,因此就嘗試使用Vue來實現,將這個聯動抽離成爲一個單獨的組件,廢話少說,先來一張效果圖。數據結構

Vue實現左右菜單聯動效果圖

這個組件分爲兩個部分,一、左菜單;二、右菜單。app

動態數據結構
動態數據結構函數

menus: [
  {
    name: '菜單1',
    data: [
      {
        name: '1.1'
      },
      {
        name: '1.2'
      },
      {
        name: '1.3'
      },
      {
        name: '1.4'
      },
      {
        name: '1.5'
      },
      {
        name: '1.6'
      }
    ]
  }
]

data數據是用戶自定義增長一些內容,並渲染DOMpost

左菜單的DOM結構ui

<scroll
  class="left-menu"
  :data="menus"
  ref="leftMenu">
  <div class="left-menu-container">
    <ul>
      <li
        class="left-item"
        ref="leftItem"
        :class="{'current': currentIndex === index}"
        @click="selectLeft(index, $event)"
        v-for="(menu, index) in menus"
        :key="index">
        <p class="text">{{menu.name}}</p>
      </li>
    </ul>
  </div>
</scroll>

右菜單的DOM結構this

<scroll
  class="right-menu"
  :data="menus" 
  ref="rightMenu"
  @scroll="scrollHeight"
  :listenScroll="true"
  :probeType="3">
  <div class="right-menu-container">
    <ul>
      <li class="right-item" ref="rightItem" v-for="(menu, i) in menus" :key="i">
        <div class="title">{{menu.name}}</div>
        <ul>
          <li v-for="(item, j) in menu.data" :key="j">
            <div class="data-wrapper">
              <div class="data">{{item.name}}</div>
            </div>
          </li>
        </ul>
      </li>
    </ul>
  </div>
</scroll>

這裏是爲了作demo,因此在數據上只是單純捏造。

固然由於這是個子組件,咱們將經過父組件傳遞props,因此定義props

props: {
    menus: {
      required: true,
      type: Array,
      default () {
        return []
      }
    }
  },

原理圖

在這個業務場景中,咱們的實現方式是根據右邊菜單滾動的高度來計算左邊菜單的位置,固然左邊菜單也能夠經過點擊來肯定右邊菜單須要滾動多高的距離,那麼咱們如何得到該容器滾動的距離呢?
以前一直在使用better-scroll,經過閱讀文檔,咱們知道它有有scroll事件,咱們能夠經過監聽這個事件來獲取滾動的pos
scroll事件

if (this.listenScroll) {
  let me = this
  this.scroll.on('scroll', (pos) => {
    me.$emit('scroll', pos)
  })
}

因此咱們在右邊菜單的scroll組件上監聽scroll事件

@scroll="scrollHeight"

method

scrollHeight (pos) {
  console.log(pos);
  this.scrollY = Math.abs(Math.round(pos.y))
},

咱們將監聽獲得的pos打出來看看
監聽scroll事件,獲得pos

咱們能夠看到控制檯打出了當前滾動的pos信息,由於在移動端開發時,座標軸和咱們數學中的座標軸相反,因此上滑時y軸的值是負數

移動開發座標軸

因此咱們要獲得每一塊li的高度,咱們能夠經過拿到他們的DOM

_calculateHeight() {
  let lis = this.$refs.rightItem;
  let height = 0
  this.rightHeight.push(height)
  Array.prototype.slice.call(lis).forEach(li => {
    height += li.clientHeight
    this.rightHeight.push(height)
  })
console.log(this.rightHeight)
}

咱們在created這個hook以後調用這個計算高度的函數

_calculateHeight() {
  let lis = this.$refs.rightItem;
  let height = 0
  this.rightHeight.push(height)
  Array.prototype.slice.call(lis).forEach(li => {
    height += li.clientHeight
    this.rightHeight.push(height)
  })
  console.log(this.rightHeight)
}

獲得右邊菜單高度

當用戶在滾動時,咱們須要計算當前滾動距離實在那個區間內,並拿到他的index

找到滾動位置對應的index
找到滾動位置對應的index

computed: {
  currentIndex () {
    const { scrollY, rightHeight } = this
    const index = rightHeight.findIndex((height, index) => {
      return scrollY >= rightHeight[index] && scrollY < rightHeight[index + 1]
    })
    return index > 0 ? index : 0
  }
}

因此當前應該是左邊菜單index = 1的菜單項active
以上是左邊菜單根據右邊菜單的滑動聯動的實現,用戶也能夠經過點擊左邊菜單來實現右邊菜單的聯動,此時,咱們給菜單項加上click事件

@click="selectLeft(index, $event)"

這裏加上$event是爲了區分原生點擊事件仍是better-scroll派發的事件

selectLeft (index, event) {
  if (!event._constructed) {
    return
  }
  let rightItem = this.$refs.rightItem
  let el = rightItem[index]
  this.$refs.rightMenu.scrollToElement(el, 300)
},

使用

<cascad-menu :menus="menus"></cascad-menu>

到這裏咱們就基本上完成了這些需求了

相關文章
相關標籤/搜索