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>

到这里咱们就基本上完成了这些需求了

相关文章
相关标签/搜索