Vue實現左右選單聯動實現(更新)

Rain120發表於2018-08-11

title: Vue實現左右選單聯動實現 date: 2018-08-11 16:31:34 tags:

  • Vue
  • 左右聯動 top: 100 copyright: true

知乎

個人部落格

Github

原始碼傳送門:Rain120/vue-study

根據掘金評論需求,更新了資料介面並修復了一些問題

之前在外賣軟體上看到這個左右聯動的效果,覺得很有意思,所以就嘗試使用Vue來實現,將這個聯動抽離成為一個單獨的元件,廢話少說,先來一張效果圖。

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

這個元件分為兩個部分,1、左選單;2、右選單。

動態資料結構

動態資料結構

menus: [
  {
    name: '選單1',
    data: [
      {
        name: '1.1'
      },
      {
        name: '1.2'
      },
      {
        name: '1.3'
      },
      {
        name: '1.4'
      },
      {
        name: '1.5'
      },
      {
        name: '1.6'
      }
    ]
  }
]
複製程式碼

data資料是使用者自定義增加一些內容,並渲染DOM

左選單的DOM結構

<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結構

<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>
複製程式碼

到這裡我們就基本上完成了這些需求了

相關文章