首页 > 网站 > WEB开发 > 正文

better-scroll插件初使用

2024-04-27 15:03:52
字体:
来源:转载
供稿:网友

模仿了一个饿了吗页面先上图,看一看better-scroll的效果

better-scroll
如果用overflow:auto或scroll属性,这样的页面就会出现两个滚动条,用户体验会有折扣,所以给介绍一款插件better-scroll,附上github资源链接。这款插件是基于iscroll插件做的重新封装,改善了一些bug,增加了一些拓展功能(插件作者并不是本人,望周知),有兴趣的小伙伴们可以自行下载体验体验。那么今天就用bs插件配合vue框架对这个页面重构一下,主要实现以下几点功能。
- 1、左右侧页面滑动,并且不显示滚动条。 - 2、根据左侧的商品类别对应到右侧相应的选择区间,并且高亮标题。 - 3、根据右侧用户滑动的区间,能够对应到左侧的商品类别,并且高亮选择标题。 ps:除了用到bs插件的一些基础功能以外,还有一些vue的基础知识。

前期准备

我引入了reset.CSS对页面的样式做一个算是初始化吧,然后引入js脚本有vue.js,vue-resource.js,bscroll.js,可以进入vue官网下载。

页面布局

页面布局和样式就不浪费时间了,直接上代码了

样式代码
[v-cloak] { display: none; } .goods { position: absolute; width: 100%; top: 174px; bottom: 46px; display: flex; overflow: hidden; } .goods .menu-wrapper { flex: 0 0 80px; width: 80px; background: #f3f5f7; } .goods .menu-wrapper .current { position: relative; z-index: 10; margin-top: -1px; background: #FFFFFF; font-weight: 700; font-size: 14px; } .goods .menu-wrapper .menu-item { display: table; height: 54px; width: 80px; line-height: 14px; padding: 0 12px; border-bottom: 1px solid rgba(7, 17, 27, .1); box-sizing: border-box; } .goods .menu-wrapper .menu-item .icon { display: inline-block; width: 12px; height: 12px; margin-right: 2px; -webkit-background-size: 12px 12px; background-size: 12px 12px; background-repeat: no-repeat; vertical-align: top; } .goods .menu-wrapper .menu-item .text { display: table-cell; width: 56px; vertical-align: middle; font-size: 12px; text-align: center; } .goods .menu-wrapper .menu-item .decrease { background-image: url(img/decrease_2@2x.png); } .goods .menu-wrapper .menu-item .discount { background-image: url(img/decrease_2@2x.png); } .goods .menu-wrapper .menu-item .guarantee { background-image: url(img/decrease_2@2x.png); } .goods .menu-wrapper .menu-item .invoice { background-image: url(img/decrease_2@2x.png); } .goods .menu-wrapper .menu-item .special { background-image: url(img/decrease_2@2x.png); } .goods .foods-wrapper { flex: 1; } .goods .foods-wrapper .title { padding-left: 14px; height: 26px; line-height: 26px; border-left: 2px solid #d9dde1; font-size: 12px; color: rgb(147, 153, 159); background: #F3F5F7; } .goods .foods-wrapper .current { color: #42B983; font-size: 14px; transition: all .5s; line-height: 27px; } .goods .foods-wrapper .food-item { display: flex; margin: 18px 0 18px 0; border-bottom: 1px solid rgba(7, 17, 27, .1); padding-bottom: 18px; } .goods .foods-wrapper .food-item:last-child { border-bottom: 0px solid rgba(7, 17, 27, .1); margin-bottom: 0; } .goods .foods-wrapper .food-item .icon { flex: 0 0 57px; margin-right: 10px; margin-left: 10px; } .goods .foods-wrapper .food-item .content { position: relative; flex: 1; } .goods .foods-wrapper .food-item .content .name { margin: 2px 0 8px 0; height: 14px; line-height: 14px; font-size: 14px; color: rgb(7, 17, 27); } .goods .foods-wrapper .food-item .content .desc { margin-bottom: 8px; line-height: 10px; font-size: 10px; color: rgb(147, 153, 159); } .goods .foods-wrapper .food-item .content .extra { font-size: 10px; color: rgb(147, 153, 159); line-height: 10px; } .goods .foods-wrapper .food-item .content .extra .count { margin-right: 12px; } .goods .foods-wrapper .food-item .content .PRice { font-weight: 700; line-height: 24px; } .goods .foods-wrapper .food-item .content .price .now { margin-right: 8px; font-size: 14px; color: rgb(240, 20, 20); } .goods .foods-wrapper .food-item .content .price .old { text-decoration: line-through; font-size: 10px; color: rgb(147, 153, 159); } .goods .foods-wrapper .food-item .content .cartcontrol-wrapper { position: absolute; right: 6px; bottom: 12px; }
页面布局代码
<div class="goods" v-cloak> <div class="menu-wrapper" ref="menuwrapper"> <ul> <!--当currentIndex与index相等的时候,设置高亮--> <li v-for="(item,index) in goods" class="menu-item" :class="{'current':currentIndex === index}" @click="selectMenu(index,$event)" v-cloak> <span class="text"> <span v-show="item.type>0" class="icon" :class="classMap[item.type]" v-cloak></span> {{item.name}} </span> </li> </ul> </div> <div class="foods-wrapper" ref="foodwrapper"> <ul> <!--food-list-hook用于dom操作,获取整体容器的高度--> <li v-for="(item,index) in goods" class="food-list food-list-hook" v-cloak> <h2 class="title" :class="{'current':currentIndex === index}">{{item.name}}</h2> <ul> <li @click="selectfood(food,$event)" v-for="food in item.foods" class="food-item"> <div class="icon"> <img :src="food.icon" /> </div> <div class="content"> <h2 class="name">{{food.name}}</h2> <p class="desc">{{food.description}}</p> <div class="extra"> <span class="count">月售{{food.sellCount}}份</span> <span>好评率{{food.rating}}%</span> </div> <div class="price"> <span class="now">¥{{food.price}}</span> <span v-show="food.oldPrice" class="old">¥{{food.oldPrice}}</span> </div> </div> </li> </ul> </li> </ul> </div> </div>

js代码

首先在data内定义几个变量 goods:[], listHeight:[], scrollY:0,在created中请求准备好的json数据 this.$http.get('./data.json').then((res) => { if(res.status === ERR_OK) { res = res.body.goods; this.goods = res; } })首先我们已经引入了bs插件,我们先让左右两侧的被隐藏的部分滚动起来,在methods方法里面定义一个_initScroll的函数,主要用来对左右两侧dom结构进行初始化。用better-scroll的方法初始化需要滚动的dom结构,vue为我们提供了一个方法可以便利的获取到dom结构,我们在需要获取dom结构的父容器内添加ref="foodwrapper" ,然后在函数内用this.$refs.menuwrapper获取到dom。 然后在Ajax内执行_initScroll() 函数,这个时候需要注意两点,第一使用bs插件的时候子容器的高度一定要大于父容器的高度,才会产生滚动效果。第二,我们要等dom结构完全加载结束在调用_initScroll()方法才会生效,vue的作者也为我们提供了方法,来判断dom结构是否完全加载this.$nextTick(() => {})click: true属性用来设置可以进行点击事件。 _initScroll() { this.meunScroll = new BScroll(this.$refs.menuwrapper, { click: true }); this.foodScroll = new BScroll(this.$refs.foodwrapper, { click: true }); }这时候created应改为 created() { this.classMap = ['decrease', 'discount', 'guarantee', 'invoice', 'special']; this.$http.get('./data.json').then((res) => { if(res.status === ERR_OK) { res = res.body.goods; this.goods = res; //dom结构加载结束 this.$nextTick(() => { this._initScroll(); }) } }); }, 下面实现左右联动并且实现文本的高亮,左右联动的基本原理其实我们计算出右侧实时变化的y值,落到哪一个区间,我们就显示那一个区间。首先我们要计算整体区间的一个高度,然后分别计算第一个区间的高度,第二个区间的高度,以此类推。然后将区间数存入一个定义好的数组。当我们在滚动的时候实时拿到y轴的高度,然后对比在哪一个区间,这样我们就会得到一个区间的索引值去对应左侧的菜品类别,最后我们用一个vue的class去绑定高亮文本。 定义一个方法在_initScroll下面,作为计算高度的方法叫做_calculateHeight () ,在定义一个listHeight:[]数组,存放获取的高度。我们在定义一个food-list-hook类,用来被js选择。不要忘记在created内调用函数。_calculateHeight () { let foodList = this.$refs.foodwrapper.getElementsByClassName('food-list-hook'); let height = 0; //把第一个高度送入数组 this.listHeight.push(height); //通过循环foodList下的dom结构,将每一个li的高度依次送入数组 for(let i=0; i<foodList.length; i++){ let item = foodList[i] height += item.clientHeight this.listHeight.push(height); } },我们获取到区间高度数组后,我们要实时获取到右侧的y值,和左侧的索引值做一个对比,定义一个scrollY变量用来存放实时获取的y值。bs插件为我们提供了一个实时获取y值的方法,我们在初始化this.foodScroll的时候加一个·属性probeType: 3,其作用就是实时获取y值,相当于探针的作用。 我们在添加一个方法this.foodScroll.on('scroll',(pos) => {}),作用是实时滚动的时候把获取到的位置给暴露出来。代码如下。 methods: { _initScroll() { this.meunScroll = new BScroll(this.$refs.menuwrapper, { click: true }); this.foodScroll = new BScroll(this.$refs.foodwrapper, { click: true, //探针作用,实时监测滚动位置 probeType: 3 }); //设置监听滚动位置 this.foodScroll.on('scroll', (pos) => { //scrollY接收变量 this.scrollY = Math.abs(Math.round(pos.y)); }) }, _calculateHeight() { let foodList = this.$refs.foodwrapper.getElementsByClassName('food-list-hook'); let height = 0; //把第一个高度送入数组 this.listHeight.push(height); //通过循环foodList下的dom结构,将每一个li的高度依次送入数组 for(let i = 0; i < foodList.length; i++) { let item = foodList[i] height += item.clientHeight this.listHeight.push(height); } }, }定义一个计算属性computed,用来计算左侧对应的i值,从而定位到左侧边栏的位置 computed:{ currentIndex () { for(let i=0; i<this.listHeight.length; i++){ //判断当currentIndex在height1和height2之间的时候显示 let height1 = this.listHeight[i]; let height2 = this.listHeight[i+1]; //最后一个区间没有height2 if(!height2 || (this.scrollY >= height1 && this.scrollY < height2)){ return i; } } return 0; },获取到i后,在menu-item绑定一个class:class="{'current':currentIndex === index}",当currentIndex和menu-item对应的index相等时,设置current的样式。这样就可以左右联动了。 最后实现左侧点击的功能。在左侧的li下绑定一个selectMenu的点击事件,并传入索引值,这样我们就可以知道点击的是哪一个li selectMenu (index,event) {// 自己默认派发事件时候(BScroll),_constructed被置为true,但是浏览器原生并没有这个属性 if (!event._constructed){ return; } //运用BScroll接口,滚动到相应位置 let foodList = this.$refs.foodwrapper.getElementsByClassName('food-list-hook'); //获取对应元素的列表 let el = foodList[index]; //设置滚动时间 this.foodScroll.scrollToElement(el, 300); },至此,我们就用bs插件完成了这个左右页面联动的效果,代码会上传到github,有兴趣的可以下载下来看一看,大神务喷!完整的js代码 <script type="text/javascript"> var ERR_OK = 200; new Vue({ el: '.goods', data() { return { msg: 'goods', goods: [], listHeight: [], scrollY: 0, } }, created() { this.classMap = ['decrease', 'discount', 'guarantee', 'invoice', 'special']; this.$http.get('./data.json').then((res) => { if(res.status === ERR_OK) { res = res.body.goods; this.goods = res; //dom结构加载结束 this.$nextTick(() => { this._initScroll(); //计算高度 this._calculateHeight(); }) } }); }, computed: { currentIndex() { for(let i = 0; i < this.listHeight.length; i++) { //判断当currentIndex在height1和height2之间的时候显示 let height1 = this.listHeight[i]; let height2 = this.listHeight[i + 1]; // console.log('height1:'+height1+','+'height2:'+height2) //最后一个区间没有height2 if(!height2 || (this.scrollY >= height1 && this.scrollY < height2)) { return i; } } return 0; } }, methods: { selectMenu(index, event) { // 自己默认派发事件时候(BScroll),_constructed被置为true,但是浏览器原生并没有这个属性 if(!event._constructed) { return; } //运用BScroll接口,滚动到相应位置 let foodList = this.$refs.foodwrapper.getElementsByClassName('food-list-hook'); //获取对应元素的列表 let el = foodList[index]; this.foodScroll.scrollToElement(el, 300); }, _initScroll() { this.meunScroll = new BScroll(this.$refs.menuwrapper, { click: true }); this.foodScroll = new BScroll(this.$refs.foodwrapper, { click: true, //探针作用,实时监测滚动位置 probeType: 3 }); //设置监听滚动位置 this.foodScroll.on('scroll', (pos) => { //scrollY接收变量 this.scrollY = Math.abs(Math.round(pos.y)); }) }, _calculateHeight() { let foodList = this.$refs.foodwrapper.getElementsByClassName('food-list-hook'); let height = 0; //把第一个高度送入数组 this.listHeight.push(height); //通过循环foodList下的dom结构,将每一个li的高度依次送入数组 for(let i = 0; i < foodList.length; i++) { let item = foodList[i] height += item.clientHeight this.listHeight.push(height); } }, } }) </script>
完整项目效果演示

better-scroll


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表