在移动端设计页面滚动时,往往希望它出现一定的效果,比如惯性滚动、弹簧效果等,那么原生的滚动就显得有些平淡了,这里推荐一下Better-scroll插件,它是在iscroll的基础上创新的,并且近期一直在更新,还是很适合使用的
一.那么接下来看一下它的基本使用:
- BScroll中,父容器中只能嵌套一个元素,通常我们会把这个父容器的类取名为wrapper,一个wrapper中只能包含一个元素,而包含的元素内部可以嵌套多个元素
- 父容器wrapper需要设置一定的高度,即height。此外通过
overflow:hidden
隐藏外部的元素,使元素都显示在设定的高度内。
<template>
<div class="wrapper" ref="wrapper">
<ul class="content">
<li></li>
<li></li>
</ul>
</div>
</template>
const bscroll = new BScroll(this.$refs.wrapper,{
//这里添加属性
probeType: 3, // 默认情况下BScroll是不可以实现监听位置的,需要这是probeType的值来监听
click: true,
pullUpLoad: true
})
bscroll.on('scroll',(position) => {
console.log(position)
})
bscroll.on('pullingUp',() => {
console.log("上拉加载更多");
//发送网络请求,请求更多页的数据
//等数据请求完成,并且将新的数据展示出来后
setTimeout(() => {
bscroll.finishPullUp() // 必须要调用finishPullUp才能执行下一次上拉加载
},2000)
})
- probeType属性有四个值:0,1都是不侦测实时位置;2表示在手指滚动时侦测,手指离开后的惯性滚动过程中不侦测;3表示只要是滚动都会侦测
二.Better-scroll的封装
在封装时,我们最好单独创建一个名为Scroll.vue的文件,封装好scroll组件,之后将其导入其他组件中
<template>
<div class="wrapper" ref="wrapper">
<div class="content">
<slot></solt>
</div>
</div>
</template>
<script>
import BScroll from 'Better-scroll'
export default {
name: "scroll",
data(){
return {
scroll: null
}
},
mounted() {
this.scroll = new BScroll(this.$refs.wrapper,{
})
}
}
</script>
在其他组件内导入、注册后,使用<scroll></scroll>
自定义标签就可以将其标签内部的元素添加滚动
且需要为scroll标签设置类名,以分配高度,因为在不同的组件中需要滚动效果的高度是不一样的
<scroll class="content">
其他自定义标签
</scroll>
<style scoped>
.content { // 除了上下控制栏,余下高度全部滚动,此时不能设置固定高度,因为不同的设备尺寸有差异
overflow: hidden;
position: absolute;
top: 44px;
bottom: 49px;
left: 0;
right: 0;
}
</style>
三.解决Better-scroll可滚动区域的问题
1.Better-Scroll在决定有多少区域可以滚动时,是根据scrollerHeight决定的
scrollerHeight属性是根据Better-Scroll中的content中的子组件的高度,但是在我们首页中,刚开始计算scrollHeight属性时,是没有将图片计算在内的,因为图片属于异步加载,所以此时计算出来的高度是错误的,后来图片加载进来后有了新的高度,但是scrollerHeight属性并没有更新,所以滚动出现了问题。
2.如何解决:
针对于解决方法,可以采取监听每一张图片是否加载完成,只要有一张图片加载完成,就执行一次refresh()
3.在Vue中采用@load='方法'
的方式进行监听
4.那么要如何将子组件里的事件传到首页去呢?因为这里可能会涉及到非父子组件的通信,所以这里我们选择了事件总线的方法。
- 创建事件总线的方法:
- 在mian.js文件中添加: Vue.prototype.$bus = new Vue()
- 在子组件中添加方法:this.$bus. $emit(‘事件名称’,参数)
- 在首页中添加:this.$bus. $on(‘事件名称’,回调函数(参数))
在item组件中
<template>
<div class="goods-item">
<img :src="goodsItem.show.img" alt="" @load="imageLoad"> // @load
</div>
</template>
<script>
methods: {
imageLoad() {
this.$bus.$emit('itemImageLoad') // 通过$bus.$emit将事件发送到事件总线中
}
}
</script>
在Home组件中
created() {
//监听item中图片加载完成
this.$bus.$on('itemImageLoad',() => {
this.$refs.scroll.refresh() // 图片每加载一次,scroll刷新一次
})
}
// 在main.js中
Vue.prototype.$bus = new Vue()
四.对refresh非常频繁的问题,进行防抖操作
- 防抖函数debounce起作用的过程:
- 如果我们直接执行refresh,那么refresh函数会被执行很多次(图片每加载一次,就会执行一次)
- 可以将refresh函数传入到debounce函数中,生成一个新函数
- 之后再调用非常频繁时,就使用新生成的函数
- 而新生成的函数并不会非常频繁的调用,如果下一次执行的非常快,就会将上一次的调用取消
- 最终当所有图片均加载完成后,滚动将会刷新一次
mounted() {
//监听item中图片加载完成
const refresh = this.debounce(this.$refs.scroll.refresh,50)
this.$bus.$on('itemImageLoad',() => {
refresh()
})
},
methods: {
debounce(func , delay) {
let timer = null
return function(...args) {
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this , args)
},delay)
}
}
}
通常情况下,我们会将debounce防抖函数放在common文件夹下->utils.js文件中,以便被其他组件使用