better-scroll实现列表联动(移动端)

在移动端手机滑动浏览列表是没有滚动条的,那么如何实现没有滚动条又能够滚动呢?

这时候会用到一个常用的插件:better-scroll,利用它来实现移动端的滚动。其原理是利用translate来实现滚动,看下调试:在这里插入图片描述
这个div是需要滚动的列表的父元素,transition-timing-function是为了让translate这个过渡效果更像原生scroll。

使用这个组件你需要有特定的结构,借一张图:在这里插入图片描述
wrpper有固定高度content区超出隐藏;

使用时首先引入better-scroll插件:

import Better from 'better-scroll

传入dom节点对象:

new Better(wrpper)

wrpper节点对象可以是自己找到的对象,赋值给一个变量并传入,也可以传入节点的class名或者id它会自动调用querySelector方法去找节点,还可以通过$refs来查找。
给节点设置ref


传入:

new Better(this.$refs.list)

这样改wrpper内部的content区就可以实现滚动了;
然后来看看这种效果:在这里插入图片描述
左右两个列表,左边点击右边跳到相应位置,右边滚动左边相应位置也高亮显示;这就是典型的列表联动。
我们将它分解一下,先实现左联右:
用到better-scroll中的 scrollToElement(el,time,offsetX,offsetY,easing) 很简单的方法拿来即用
1、在需要跳转到的区块上添加ref属性用于标识

1        <div v-for="(it,index) in goods" :key="index" ref="good">
 2         <p class="title">
 3           {{it.name}}
 4         </p>
 5         <ul>
 6           <li  v-for="(item,index) in it.foods" :key="index" class="list " >
 7             <div class="list_item flex" :class="index===it.foods.length-1?'last':''">
 8               <p @click="getDetails(item)"><img :src="item.icon" alt=""></p>
 9               <div>
10                 <p class="name">{{item.name}}</p>
11                 <p class="des">{{item.description}}</p>
12                 <p class="sale">月售{{item.sellCount}}份<span>好评率{{item.rating}}%</span></p>
13                 <p class="price">¥{{item.price}}</p>
14                 <div class="add"><add :food="item"></add></div>
15               </div>
16             </div>
17           </li>
18         </ul>
19       </div>

2、定义方法跳转到相应位置

1methods: {
2    change (index) {
3      this.actli = index
4      this.rgt.scrollToElement(this.$refs.good[index], 100, 0, 0)
5    },
6}

3、左列表点击调用

1     <aside ref="l_list">
 2        <ul>
 3          <li ref="l_item" :class="{'act':index === actli}" 
 4		@click="change(index)" v-for="(item,index) in goods" :key="index">
 5             <p>
 6               {{item.name}}
 7             </p>
 8           </li>
 9        </ul>
10    </aside>

记住左侧滚动栏的better-scroll对象要开启点击事件

1      this.left = new Better(this.$refs.l_list, {
2        click: true //开启点击事件
3      })

在这里插入图片描述
然后来实现:右联左
右边在滚动时我们需要知道它滚动了多少距离,滚动到了哪里,(右边属于列表套列表,大列表是分类,小列表是具体商品)
滚动的距离可以通过better-scroll对象的scroll事件的回调函数的参数取得;

1    this.rgt = new Better(this.$refs.r_list, {
2        click: true,
3        probeType: 3//实时分发scroll事件
4      })
5    this.rgt.on('scroll', (res) => {
6          console.log(res)
7          this.scrollY = Math.abs(res.y)
8      })

probeType属性具体可参考官网文档,在此不再累述
在这里插入图片描述
可以看到参数res是一个对象,对象内包含x,y,即滚动的x轴和y轴的距离。

知道了滚动了多少距离,还要知道滚动到了哪里,即大列表的index值;我们可以拿content滚动的距离和每个小列表距离顶部的距离作比较;

设content滚动距离为x,小列表a距离顶部的距离为La,小列表b距离顶部的距离为Lb(小列表a是小列表b的前一项),那么当La<x<Lb时,我们可以确定content滚动到了小列表a的区域,那么此时大列
表index值为小列表a的索引值;

初始化一个数组用于存储小列表距离顶部的距离:

1   data () {
2    return {
3      actli: 0,
4      arr: [0],
5    }
6  },

将距离存储到arr中:

1 mounted () {
2      this.$refs.good.forEach((el, index) => {
3        this.arr.push(el.offsetHeight + this.arr[index])
4      })
5}

比较并更新index:

1   this.rgt.on('scroll', (res) => {
 2        if (this.flag) {
 3          console.log(res)
 4          this.scrollY = Math.abs(res.y)
 5          for (let i = 0; i < this.arr.length; i++) {
 6            if (this.scrollY > this.arr[i] && this.scrollY < this.arr[i + 1]) {
 7              this.actli = i
 8            }
 9          }
10        }
11      })

考虑到数据的异步请求,以及左侧列表增加动态性更完整的代码如下:

1mounted () {
 2    setTimeout(() => {
 3      /* eslint-disable no-new */
 4      this.left = new Better(this.$refs.l_list, {
 5        click: true //开启点击事件(默认关闭)
 6      })
 7      this.rgt = new Better(this.$refs.r_list, {
 8        click: true,
 9        probeType: 3  //scroll事件实时分发
10      })
11      this.$refs.good.forEach((el, index) => {//计算每个列表相对于顶部的距离,存到数组arr中
12        this.arr.push(el.offsetHeight + this.arr[index])
13      })
14      this.rgt.on('scroll', (res) => {//监听滚动事件
15        if (this.flag) {
16          this.scrollY = Math.abs(res.y)
17          for (let i = 0; i < this.arr.length; i++) {
18            if (this.scrollY > this.arr[i] && this.scrollY < this.arr[i + 1]) {
19              this.actli = i
20              if (i === this.$refs.l_item.length - 2) {//当滚动到倒数第2个位置时左侧列表向上滚动一个距离
21                this.left.scrollToElement(this.$refs.l_item[1], 100, 0, 0)
22              }
23              if (i === 2) {//当滚动到倒数第3个位置时左侧列表向上下滚动一个距离
24                this.left.scrollToElement(this.$refs.l_item[0], 100, 0, 0)
25              }
26            }
27          }
28        }
29      })
30    })
31  }

附goods组件全部代码:

template:

 1<template>
 2  <div v-if="goods">
 3    <aside ref="l_list">
 4        <ul>
 5          <li ref="l_item" :class="{'act':index === actli}" 
 6@click="change(index)" v-for="(item,index) in goods" :key="index">
 7            <p>
 8              {{item.name}}
 9            </p>
10          </li>
11        </ul>
12    </aside>
13   <section class="r_list" ref="r_list">
14     <div >
15       <div v-for="(it,index) in goods" :key="index" ref="good">
16         <p class="title">
17           {{it.name}}
18         </p>
19         <ul>
20           <li  v-for="(item,index) in it.foods" :key="index" class="list " >
21             <div class="list_item flex" :class="index===it.foods.length-1?'last':''">
22               <p @click="getDetails(item)"><img :src="item.icon" alt=""></p>
23               <div>
24                 <p class="name">{{item.name}}</p>
25                 <p class="des">{{item.description}}</p>
26                 <p class="sale">月售{{item.sellCount}}份<span>好评率{{item.rating}}%</span></p>
27                 <p class="price">¥{{item.price}}</p>
28                 <div class="add"><add :food="item"></add></div>
29               </div>
30             </div>
31           </li>
32         </ul>
33       </div>
34     </div>
35   </section>
36  </div>
37</template>

js:

 1<script>
 2import Better from 'better-scroll'
 3import add from '../addNum/addnum'
 4export default {
 5  name: 'goods',
 6  data () {
 7    return {
 8      actli: 0,
 9      scrollY: 0,
10      arr: [0],
11      flag: true,
12      obj: null,
13      show: false
14    }
15  },
16  components: {add},
17  computed: {
18    goods () {
19      return this.$store.state.msg.goods
20    }
21  },
22  watch: {
23    show () {
24      /* eslint-disable no-new */
25      if (this.show) {
26        setTimeout(() => {
27          new Better(this.$refs.detail, {
28            click: true
29          })
30        })
31      }
32    }
33  },
34  methods: {
35    change (index) {
36      this.flag = false
37      this.actli = index
38      this.rgt.scrollToElement(this.$refs.good[index], 100, 0, 0)
39      setTimeout(() => {
40        this.flag = true
41      }, 100)
42    },
43    getDetails (it) {
44      this.show = !this.show
45      this.obj = it
46    }
47  },
48  mounted () {
49    setTimeout(() => {
50      /* eslint-disable no-new */
51      this.left = new Better(this.$refs.l_list, {
52        click: true
53      })
54      this.rgt = new Better(this.$refs.r_list, {
55        click: true,
56        probeType: 3
57      })
58      this.$refs.good.forEach((el, index) => {
59        this.arr.push(el.offsetHeight + this.arr[index])
60      })
61      this.rgt.on('scroll', (res) => {
62        if (this.flag) {
63          console.log(res)
64          this.scrollY = Math.abs(res.y)
65          for (let i = 0; i < this.arr.length; i++) {
66            if (this.scrollY > this.arr[i] && this.scrollY < this.arr[i + 1]) {
67              this.actli = i
68              if (i === this.$refs.l_item.length - 2) {
69                this.left.scrollToElement(this.$refs.l_item[1], 100, 0, 0)
70              }
71              if (i === 2) {
72                this.left.scrollToElement(this.$refs.l_item[0], 100, 0, 0)
73              }
74            }
75          }
76        }
77      })
78    })
79  }
80}
81</script>
82

css:

 1<style scoped lang="less">
  2aside{
  3  position: fixed;
  4  left: 0;
  5  top: 3.48rem;
  6  bottom: 1.04rem;
  7  overflow: hidden;
  8  ul{
  9    li{
 10      height: 1.08rem;
 11      width: 1.6rem;
 12      display: table;
 13      background-color: #f3f5f7;
 14      padding: 0 0.24rem;
 15      box-sizing: border-box;
 16      p{
 17        display: table-cell;
 18        font:200 0.24rem/0.28rem '';
 19        vertical-align: middle;
 20        border-bottom: 1px solid rgba(7,17,27,.1);
 21      }
 22    }
 23    .act{
 24      background-color: deepskyblue;
 25    }
 26  }
 27}
 28.r_list{
 29    position: fixed;
 30    left: 1.6rem;
 31    right: 0;
 32    top: 3.48rem;
 33    bottom: 1.04rem;
 34    overflow: hidden;
 35    .title{
 36      border-left: 3px solid #d9dde1;
 37      height: 0.52rem;
 38      font: 0.24rem/0.52rem '';
 39      color: rgb(147,153,159);
 40      background-color:#f3f5f7;
 41      padding-left: 0.28rem;
 42    }
 43    .flex{
 44      display: flex;
 45      justify-content: center;
 46      align-items: center;
 47    }
 48    .list{
 49      padding: 0.36rem 0.36rem 0 0.36rem;
 50      img{
 51        width: 1.28rem;
 52        border-radius: 0.04rem;
 53      }
 54      .list_item{
 55        justify-content: flex-start;
 56        border-bottom: 1px solid rgba(7,17,27,0.1);
 57        padding-bottom: 0.36rem;
 58        position: relative;
 59        &>div{
 60          margin-left: 0.2rem;
 61          .name{
 62            font: 0.28rem/0.28rem '';
 63            color: rgb(7,17,27);
 64            margin-bottom: 0.16rem;
 65          }
 66          .des,.sale{
 67            font: 0.2rem/0.2rem '';
 68            color: rgb(147,153,159);
 69            margin-bottom: 0.16rem;
 70          }
 71          .sale{
 72            margin-bottom: 0;
 73            span{
 74              margin-left: 0.24rem;
 75            }
 76          }
 77          .price{
 78            font:700 0.2rem/0.48rem '';
 79            color: red;
 80          }
 81          .add{
 82            position: absolute;
 83            right: 0;
 84            bottom: 0.28rem;
 85          }
 86        }
 87      }
 88      .last{
 89        border-bottom: none;
 90      }
 91    }
 92  }
 93  .detail{
 94    background-color: white;
 95    position: fixed;
 96    z-index: 299;
 97    left: 0;
 98    right: 0;
 99    top:0;
100    bottom: 1.04rem;
101    overflow: hidden;
102    .bg{
103      img{
104        height: 7.5rem;
105      }
106    }
107    .tit{
108      &>div{
109        &:nth-of-type(1){
110          padding: 0.36rem;
111          p{
112            &:nth-of-type(1){
113              font: 700 0.28rem/0.28rem '';
114              margin-bottom: 0.16rem;
115            }
116            &:nth-of-type(2){
117              font: 0.2rem '';
118              color: rgb(147,153,159);
119              margin-bottom: 0.36rem;
120            }
121            &:nth-of-type(3){
122              font:700 0.28rem/0.48rem '';
123              color: rgb(240,20,20);
124            }
125          }
126        }
127        &:nth-of-type(2){
128          float: right;
129          margin-right: 0.36rem;
130          margin-top: -0.9rem;
131          font: 0.2rem/0.48rem '';
132          color: white;
133          background-color: rgb(0,160,220);
134          border-radius: 0.24rem;
135          width: 1.48rem;
136          height: 0.48rem;
137          text-align: center;
138        }
139      }
140    }
141    .b_line{
142      height: 0.36rem;
143      background-color: #f3f5f7;
144      border-bottom: 1px solid rgba(0,0,0,0.1);
145      border-top: 1px solid rgba(0,0,0,0.1);
146    }
147    .intr{
148      padding: 0.36rem;
149      p{
150        &:nth-of-type(1){
151          font: 0.28rem '';
152          margin-bottom: 0.12rem;
153        }
154        &:nth-of-type(2){
155          font:200 0.24rem/0.48rem '';
156          color: rgba(77,85,93);
157          min-height: 1rem;
158        }
159      }
160    }
161    .flex{
162      display: flex;
163      justify-content: flex-start;
164      align-items: center;
165    }
166    .esit{
167      &>div{
168        &:nth-of-type(1){
169          padding: 0.36rem 0.36rem 0 0.36rem;
170          &>p{
171            font: 0.28rem '';
172            &:nth-of-type(1){
173              margin-bottom: 0.36rem;
174            }
175          }
176          .count {
177            padding:0 0 0.36rem 0;
178            border-bottom:1px solid rgba(7, 17, 27, 0.1) ;
179            p {
180              text-align: center;
181              font: 0.24rem/0.48rem '';
182              padding: 0.12rem;
183              border-radius: 0.02rem;
184              margin-right: 0.12rem;
185              &:nth-of-type(1) {
186                background-color: #00A0DC;
187              }
188              &:nth-of-type(2) {
189                background-color: #CCECF8;
190              }
191              &:nth-of-type(3) {
192                background-color: #E9EBEC;
193              }
194            }
195            .choosen{
196              transform: scale(1.1);
197            }
198          }
199        }
200        &:nth-of-type(2){
201          font-size: 0;
202          padding: 0.24rem 0.36rem;
203          border-bottom:1px solid rgba(7, 17, 27, 0.1) ;
204          i{
205            color: rgb(147,153,159);
206            font-size: 0.48rem;
207            margin-right: 0.08rem;
208          }
209          span{
210            font: 0.24rem '';
211            color: rgb(147,153,159);
212          }
213        }
214      }
215      &>ul{
216        li{
217          padding: 0 0.36rem;
218          &>div{
219            padding: 0.32rem 0;
220            border-bottom:1px solid rgba(7, 17, 27, 0.1) ;
221            &>div{
222              span{
223                font: 0.2rem/0.24rem '';
224                color: rgb(147,153,159);
225                &:nth-of-type(2){
226                  margin-left: 55%;
227                  margin-right: 0.12rem;
228                }
229              }
230              em{
231                font-size: 0;
232                img {
233                  width: 0.48rem;
234                  border-radius: 50%;
235                }
236              }
237            }
238            p{
239              &:nth-of-type(1){
240                font: 0.24rem/0.32rem '';
241              }
242            }
243          }
244        }
245      }
246    }
247  }

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Vue是一种用于构建用户界面的渐进式JavaScript框架,它可以帮助开发者更高效地构建单页面应用程序。而vue-better-scroll是基于Vue的一款优秀的滚动插件,它能够实现更流畅的滚动效果,并且支持上下左右的滚动联动效果。 如果需要实现左右侧菜单的联动效果,我们可以通过使用vue-better-scrollscrollToElement方法来实现。首先,我们需要在Vue中引入vue-better-scroll插件并进行配置。 在Vue实例的data中,我们可以定义左右两个菜单的数据,例如leftMenu和rightMenu,并在created生命周期中初始化数据。然后,在mounted生命周期中,我们可以通过refs属性获取到两个菜单容器的DOM元素。 接下来,我们需要监听左边菜单的点击事件,当点击左边菜单的某个选项时,我们可以通过调用vue-better-scrollscrollToElement方法,将右边菜单滚动到对应的位置。通过传递目标元素的选择器或具体的DOM元素,我们可以实现左右菜单的联动效果。 具体实现时,我们可以在左边菜单的点击事件中,使用this.$refs来访问右边菜单容器,并调用scrollToElement方法,将目标元素滚动到可视区域。通过传递选择器或具体DOM元素作为参数,我们可以精确控制滚动的位置。 同时,为了视觉上更好的效果,我们还可以给目标元素添加样式,如高亮当前选中项,以提升用户体验。 最后,通过一系列的事件处理和样式设置,我们就可以实现左右侧菜单的联动demo了。 以上就是使用Vue和vue-better-scroll实现左右侧菜单联动demo的大致思路和步骤。通过合理运用这两个工具,我们可以轻松地实现出一个流畅、友好的用户界面效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值