「敲黑板」Uniapp 实现左右联动菜单

前言

相信所有人对移动端商城app里面的分类页左右两栏联动的菜单都不陌生,几乎所有的商城分类页都是这样,这两天接触了一下uniapp,在做一个商城项目,刚好有这个需求,接下来我们用uniapp实现一下。

先看一张最终效果图:

  1. 左右两个菜单要能独立滚动;
  2. 点击左边菜单,右边滑动到相应区块;
  3. 滑动右边菜单,左边相应菜单项actived;
  4. 当右边滚动到左边相应分类超出视野范围后,左边菜单也要跟着上/下滚动。

接下来就根据需求逐步实现。

1、利用uniapp的scroll-view组件实现左右两个可独立滚动的菜单

很简单,就左右两个scroll-view,左边占20%,右边80%,这里要注意一下左右两个scroll-view外层只能包一个父级view,不然他们俩是同步滚动的。

<view class="d-flex h-100" >
  <scroll-view class="left-scroll flex-1 border-right" scroll-y>
   <view v-for="i in 50" :key="i">{{i}}</view>
  </scroll-view>
  <scroll-view class="right-scroll flex-3" scroll-y>
   <view v-for="i in 50" :key="i">{{i}}</view>
  </scroll-view>
 </view>

<script>
 export default {
 data(){
     return {
         activeIndex:0,// 左边当前菜单项的索引
         cate: [],//左边导航菜单
         list:[]// 右边菜单
     }
 },
 created(){
     this.getData()
 },
 methods:{
 getData() {
   for (let i = 0; i < 20; i++) {
    this.cate.push({
     name: '分类' + i
    })
   }
  },
 // 点击左边导航栏
 changeCate(index) {
  this.activeIndex = index
  },
  }
}
</script>

<scroll-view scroll-y class="right-scroll flex-3">
   <view class="row right-scroll-item" v-for="(item,index) in list" :key="index">
    <view class="w-100 text-center text-muted font-md">{{item.name}}</view>
    <view class="span24-8 text-center py-2" v-for="(item2 ,index2) in item.list" :key="index2">
     <image :src="item2.src" mode="" style="width: 120upx;height: 120upx;"></image>
     <text class="d-block text-light-muted font">{{item2.name}}</text>
    </view>
   </view>
  </scroll-view>

模拟data数据

getData() {
 for (let i = 0; i < 20; i++) {
  this.cate.push({
   name: '分类' + i
  })
  this.list.push({
   name: `—— 产品分类${i} ——`,
   list: []
  })
 }
 for (let i = 0; i < this.list.length; i++) {
  for (let j = 0; j < 24; j++) {
   this.list[i].list.push({
    src: '/static/images/demo/demomi.png',
    name: `分类${i}-商品${j}`
     })
   }
  }

 },

2、点击左边菜单,右边滑动到相应区块

下文中的top值指的是当前dom距离屏幕顶部的距离,也是在触发事件时该dom向上滚动的距离

这一part的思路是拿到左右两边每个dom距离屏幕顶端的距离(top),放入leftDomsTop[]和rightDomsTop[]中备用,当我们点击左边导航菜单项时,我们改变右边相同索引dom在竖直方向 向上的滚动距离(其滚动距离就是该索引对应dom距离顶部的距离),可能有点绕,多读两遍就好了。uniapp的scroll-view组件有个scroll-top属性可以很方便的改变top值。

那如何获取所有节点的top值呢,uniapp官网给我们提供了节点信息的API,我们将官网实例代码拷贝过来,改造一下:(注意:获取节点信息需要在dom挂在完成后,所以得在mounted钩子函数中写,uniapp相应的钩子函数是onReady

onReady() {
   const query = uni.createSelectorQuery().in(this);
   query.selectAll('.left-scroll-item').boundingClientRect(data => {
    this.leftDomsTop = data.map(v => v.top)
    console.log('左边top:',this.leftDomsTop)
   }).exec();
   query.selectAll('.right-scroll-item').boundingClientRect(data => {
    this.rightDomsTop = data.map(v => v.top)
    console.log('右边top:',this.rightDomsTop)
   }).exec();
  },

// html
<scroll-view scroll-y class="right-scroll flex-3" :scroll-top="rightScrollTop" scroll-with-animation></scroll-view>
// 点击左边导航栏
changeCate(index) {
 this.activeIndex = index
 // 右边scroll-view滚动到对应区块
 this.rightScrollTop = this.rightDomsTop[index]
 // console.log(this.rightScrollTop)
   },

看一下效果:

这里要注意一下,改变右边菜单top值有可能失败,如果遇到请参考官网给出的两种解决方案 传送门

3、滑动右边菜单,左边相应菜单项actived

这一part我们通过监听右边菜单的滚动事件(@scroll="onRightScroll"),可以拿到右边dom滚动的top值数组,拿到后去匹配左边相同索引的dom,将该索引值赋值给activeIndex即可。

// 监听右边滚动事件
async onRightScroll(e) {
    // 匹配当前scrollTop所处的索引
    this.rightDomsTop.forEach((v, k) => {
  if (v < e.detail.scrollTop + 3) {
   this.activeIndex = k
   return false
   }
  })
 }

这时我们差目标越来越近了,先看看现在的效果:

4、当右边滚动到左边相应分类超出视野范围后,左边菜单也要跟着上/下滚动

先简单说一下原理,我们可以监听左边菜单项activeIndex变化的时候,当左边状态为active的dom的leftDomsTop[activeIndex] + 该dom本身高度(cateItemHeight)> 左边scroll-view的高度(H)+ 其本身的top值(ST)时,我们让该dom节点向上滚动其本身高度距离(cateItemHeight),向上滚动也是同样的道理,文字很绕?结合图和公式理解一下:

接下来我们要获取以上所述需要的节点信息:

  1. 首先我们通过uniapp的fields API获取左边每个dom的尺寸及布局信息,这样就可以拿到每个dom的高度(cateItemHeight)和top值(leftScrollTop)。
onReady() {
   const query = uni.createSelectorQuery().in(this);
   query.selectAll('.left-scroll-item').fields({
       size:true,// 尺寸
       rect:true // 布局信息
   },data => {
    this.cateItemHeight = data.map(v => {
        this.cateItemHeight = v.height
        return v.top
    })
   }).exec();

  },
  1. 接着获取左边scroll-view的节点信息,并监听activeIndex的变化,然后按照上面所述判断即可
wacth:{
    activeIndex(newValue,oldValue){
        // 获取左边scroll-view的高度,top值
        const query = uni.createSelectorQuery().in(this);
        query.selectAll('#leftScroll').fields({
            size:true,
            scrollOffset:true,// 滚动状态
        },data =>{
            let H = data.height
            let ST = data.scrollTop
            // 下边
   if ((this.leftDomsTop[newValue] + this.cateItemHeight) > (H + ST)) {
    return this.leftScrollTop = this.leftDomsTop[newValue] + this.cateItemHeight - H
    }
   // 上边
   if (ST > this.cateItemHeight) {
    this.leftScrollTop = this.leftDomsTop[newValue]
    }
        })
    }
}

到此整个需求就完成了,可以返回去看看篇头的效果图了。接下来我们优化一下代码,因为我们在watch和onReady中都有获取节点信息,写了很多重复的代码,我们可以将获取节点信息封装成一个方法,用到的地方调用即可。

// 获取节点信息
getElInfo(obj = {}) {
 return new Promise((res, rej) => {
  let option = {
   size: obj.size ? true : false,
   rect: obj.rect ? true : false,
   scrollOffset: obj.scrollOffset ? true : false,
  }
 const query = uni.createSelectorQuery().in(this);
 let q = obj.all ? query.selectAll(`.${obj.all}-scroll-item`) : query.select('#leftScroll')
  q.fields(option, data => {
   res(data)
  }).exec();
  })
 },

💕看完三件事:

  1. 点赞 | 你可以点击——>收藏——>退出一气呵成,但别忘了点赞🤭
  2. 关注 | 点个关注,下次不迷路😘
  3. 也可以到GitHub拿我所有文章源文件🤗

本文使用 mdnice排版

原文链接:juejin.im

上一篇:基于Vue2.6搭建UI组件库+VuePress搭建文档
下一篇:React技术揭秘2-1 Fiber架构

相关推荐

  • 超级方便好用的微信公众号菜单管理扩展

    看了 中有一个微信管理插件,就萌生了自己做一个的想法。 展示 DEMO http://wechatmenu.lzis.me(http://wechatmenu.lzis.me) 素材选...

    1 年前
  • 自定义导航栏菜单失焦

    一个简单的效果,点击导航栏弹出菜单后,在菜单外点击触发失焦,自动关闭菜单 本文采用Vant组件 核心:通过触发菜单内的聚焦失焦控制显示/隐藏 导航栏: 菜单: 事件:...

    1 年前
  • 自定义右键单击Web应用程序的上下文菜单

    自定义右键单击Web应用程序的上下文菜单...

    2 年前
  • 简单的树形菜单checkbox关联

    tree组件 页面调用 效果图 就是这么简单粗暴;若子级全部未选中则消除对应的父级的没搞,有机会再弄 图片描述(https://img.javascriptcn.com/962f...

    2 年前
  • 简单实现JavaScript菜单栏切换效果

    分享一个小案例,实现菜单栏的切换,点击左侧边栏,展示右侧主体的页面,供大家参考,具体内容如下 首先实现html页面的编写: 其次是css效果实现: 最后一步运用简单的...

    3 年前
  • 第十六课时: 可收缩多级菜单的实现

    1、递归组件实战 通过vshow来切换remenu和icon组件的显示隐藏,remenu组件上几个课时有事例 2、vif和vshow对比 vif 是“真正的”条件渲染,因为它会确保在切换...

    1 年前
  • 禁用jQuery选择下拉菜单

    PSLgideonite(https://stackoverflow.com/users/1009603/psl)提出了一个问题:disable jquerychosen dropdown,或许与您遇...

    2 年前
  • 禁用Android上长水龙头上的上下文菜单

    JanuszRoy Sharon(https://stackoverflow.com/users/114066/janusz)提出了一个问题:Disabling the context menu on...

    2 年前
  • 用Vue撸一个『A-Z字母滑动检索菜单』

    最近用vue仿写途牛旅行APP 遇到了这样的城市列表选择页面,花了些时间,用Vue实现了一下并让它体验的接近 安卓/IOS 原生组件 很多地方都会有这样的侧边栏字母列表菜单,可以滑动实现内容列表联动...

    2 年前
  • 点击下拉菜单关闭

    Xufoxphpdev(https://stackoverflow.com/users/4642212/xufox)提出了一个问题:Avoid dropdown menu close on click...

    2 年前

官方社区

扫码加入 JavaScript 社区