首先把基本的样式写好,这里就略过了,然后引入better-scroll库
import BScroll from
'better-scroll'
其次,在mounted生命周期实例化scroll,可以获取完数据后再new,也可以先new后,获取完数据调用refresh。
//是否开启下拉刷新,可传入true或者false,如果需要更多配置可以传入一个对象
pullDownRefresh:{
threshold:80,
stop:40
}
//是否开启上拉加载,同上,上拉无stop参数,这里需要注意是负数
pullUpLoad:{
threshold:-80,
}
/**
*
* @param threshold 触发事件的阀值,即滑动多少距离触发
* @param stop 下拉刷新后回滚距离顶部的距离(为了给loading留出一点空间)
*/
对于不同缩放程度的屏幕,还需要乘以对应的缩放比。
淘宝flexible.js里面其实已经有这个获取屏幕缩放比方法,这里直接从里面拿:
//在util.js里面加一个方法
export
function
getDeviceRatio(){
var
isAndroid = window.navigator.appVersion.match(/android/gi);
var
isIPhone = window.navigator.appVersion.match(/iphone/gi);
var
devicePixelRatio = window.devicePixelRatio;
var
dpr;
if
(isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if
(devicePixelRatio >= 3) {
dpr = 3;
}
else
if
(devicePixelRatio >= 2){
dpr = 2;
}
else
{
dpr = 1;
}
}
else
{
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
return
dpr
}
import{ DEVICE_RATIO} from
'../base/js/api.js'
/*获取当前缩放比*/
const DEVICE_RATIO=getDeviceRatio();
/*下拉配置*/
const DOWN_CONFIG={
threshold:80*DEVICE_RATIO,
stop:40*DEVICE_RATIO
}
/*上拉配置*/
const UP_CONFIG={
threshold:-80*DEVICE_RATIO,
}
this
.scroller =
new
BScroll(scrollWrap,{
click:
true
,
probeType:3,
pullDownRefresh:DOWN_CONFIG,
pullUpLoad:UP_CONFIG
});
实例化后,接下来就是监听上拉和下拉事件了。betterScroll新增了一些事件,主要的有:
/*下拉事件*/
this
.scroller.on(
'pullingDown'
,()=> {});
/*上拉事件*/
this
.scroller.on(
'pullingUp'
,()=>{});
触发上拉或者下拉事件后,需要我们调用 this.scroller.finishPullDown() 或者 this.scroller.finishPullUp() 来通知better-scroll事件完成。
大致的流程是这样的:
this
.scroller.on(
'pullingDown'
,()=> {
<!-- 1. 发送请求获取数据 -->
<!-- 2. 获取成功后,通知事件完成 -->
<!-- 3. 修改data数据,在nextTick调用refresh -->
});
通常操作完成后都需要我们手动触发refresh方法来重新计算可滚动的距离,因此可以写一个watch监听数据的变化,这样我们只需要改变数据,不用每次操作数据后都调用refresh方法。
watch:{
dataList(){
this
.$nextTick(()=>{
this
.scroller.refresh();
})
}
},
如果你使用的版本还是旧的,那可以在on( scroll )事件的时候进行判断来实现功能
this
.scroller.on(
"scroll"
,(pos)=>{
//获取整个滚动列表的高度
var
height=getStyle(scroller,
"height"
);
//获取滚动外层wrap的高度
var
pageHeight=getStyle(scrollWrap,
"height"
);
//触发事件需要的阀值
var
distance=80*DEVICE_RATIO;
//参数pos为当前位置
if
(pos.y>distance){
//console.log("下拉");
//do something
}
else
if
(pos.y-pageHeight<-height-distance){
//console.log("上拉");
//do something
}
为了防止多次触发,需要加2个开关类的东西;
var
onPullUp=
true
;
var
onPullDown=
true
;
每次触发事件时,將对应的开关设置为false, 等操作完成后,再重新设置为true,否则多次下拉或者上拉就会触发多次事件。通过设置开关可以保证每次只有一个事件在进行。
最后,来封装成一个组件
<template>
<div ref=
"wrapper"
class=
"list-wrapper"
>
<div class=
"scroll-content"
>
<slot></slot>
</div>
</div>
</template>
由于每个页面需要滚动的具体内容都是不一样的,所以用了一个插槽来分发。
组件需要的参数由父级传入,通过prop来接收并设置默认值
export
default
{
props: {
dataList:{
type: Array,
default
: []
},
probeType: {
type: Number,
default
: 3
},
click: {
type: Boolean,
default
:
true
},
pullDownRefresh: {
type:
null
,
default
:
false
},
pullUpLoad: {
type:
null
,
default
:
false
},
}
组件挂载后,在事件触发时并不直接处理事件,而是向父级发送一个事件,父级通过在模板v-on接收事件并处理后续的逻辑
mounted() {
this
.scroll =
new
BScroll(
this
.$refs.wrapper, {
probeType:
this
.probeType,
click:
this
.click,
pullDownRefresh:
this
.pullDownRefresh,
pullUpLoad:
this
.pullUpLoad,
})
this
.scroll.on(
'pullingUp'
,()=> {
if
(
this
.continuePullUp){
this
.beforePullUp();
this
.$emit(
"onPullUp"
,
"当前状态:上拉加载"
);
}
});
this
.scroll.on(
'pullingDown'
,()=> {
this
.beforePullDown();
this
.$emit(
"onPullDown"
,
"当前状态:下拉加载更多"
);
});
}
父组件在使用时,需要传入配置参数Props以及处理子组件发射的事件,并且用具体的内容并替换掉 slot 标签
<Scroller
id=
"scroll"
ref=
"scroll"
:dataList=
"filmList"
:pullDownRefresh=
"DOWN_CONFIG"
:pullUpLoad=
"UP_CONFIG"
@onPullUp=
"pullUpHandle"
@onPullDown=
"pullDownHandle"
>
<ul>
<router-link class=
"film-list"
v-
for
=
"(v,i) in filmList"
:key=
"v.id"
tag=
"li"
:to=
'{path:"/film-detail/"+v.id}'
>
<div class=
"film-list__img"
>
<img v-lazy=
"v.images.small"
alt=
""
/>
</div>
<div class=
"film-list__detail"
>
<p class=
"film-list__detail__title"
>{{v.title}}</p>
<p class=
"film-list__detail__director"
>导演:{{filterDirectors(v.directors)}}</p>
<p class=
"film-list__detail__year"
>年份:{{v.year}}<span>{{v.stock}}</span></p>
<p class=
"film-list__detail__type"
>类别:{{v.genres.join(
" / "
)}}<span></span></p>
<p class=
"film-list__detail__rank"
>评分:<span>{{v.rating.average}}分</span></p>
</div>
</router-link>
</ul>
</Scroller>
父组件可以通过this.$refs.xxx来获取到子组件,可以调用子组件里面的方法;
computed:{
scrollElement(){
return
this
.$refs.scroll
}
}
完整的scroller组件内容如下
<template>
<div ref=
"wrapper"
class=
"list-wrapper"
>
<div class=
"scroll-content"
>
<slot></slot>
<div>
<PullingWord v-show=
"!inPullUp&&dataList.length>0"
:loadingWord=
"beforePullUpWord"
></PullingWord>
<Loading v-show=
"inPullUp"
:loadingWord=
'PullingUpWord'
></Loading>
</div>
</div>
<transition name=
"pullDown"
>
<Loading class=
"pullDown"
v-show=
"inPullDown"
:loadingWord=
'PullingDownWord'
></Loading>
</transition>
</div>
</template>
<script >
import BScroll from
'better-scroll'
import Loading from
'./loading.vue'
import PullingWord from
'./pulling-word'
const PullingUpWord=
"正在拼命加载中..."
;
const beforePullUpWord=
"上拉加载更多"
;
const finishPullUpWord=
"加载完成"
;
const PullingDownWord=
"加载中..."
;
export
default
{
props: {
dataList:{
type: Array,
default
: []
},
probeType: {
type: Number,
default
: 3
},
click: {
type: Boolean,
default
:
true
},
pullDownRefresh: {
type:
null
,
default
:
false
},
pullUpLoad: {
type:
null
,
default
:
false
},
},
data() {
return
{
scroll:
null
,
inPullUp:
false
,
inPullDown:
false
,
beforePullUpWord,
PullingUpWord,
PullingDownWord,
continuePullUp:
true
}
},
mounted() {
setTimeout(()=>{
this
.initScroll();
this
.scroll.on(
'pullingUp'
,()=> {
if
(
this
.continuePullUp){
this
.beforePullUp();
this
.$emit(
"onPullUp"
,
"当前状态:上拉加载"
);
}
});
this
.scroll.on(
'pullingDown'
,()=> {
this
.beforePullDown();
this
.$emit(
"onPullDown"
,
"当前状态:下拉加载更多"
);
});
},20)
},
methods: {
initScroll() {
if
(!
this
.$refs.wrapper) {
return
}
this
.scroll =
new
BScroll(
this
.$refs.wrapper, {
probeType:
this
.probeType,
click:
this
.click,
pullDownRefresh:
this
.pullDownRefresh,
pullUpLoad:
this
.pullUpLoad,
})
},
beforePullUp(){
this
.PullingUpWord=PullingUpWord;
this
.inPullUp=
true
;
},
beforePullDown(){
this
.disable();
this
.inPullDown=
true
;
},
finish(type){
this
[
"finish"
+type]();
this
.enable();
this
[
"in"
+type]=
false
;
},
disable() {
this
.scroll &&
this
.scroll.disable()
},
enable() {
this
.scroll &&
this
.scroll.enable()
},
refresh() {
this
.scroll &&
this
.scroll.refresh()
},
finishPullDown(){
this
.scroll&&
this
.scroll.finishPullDown()
},
finishPullUp(){
this
.scroll&&
this
.scroll.finishPullUp()
},
},
watch: {
dataList() {
this
.$nextTick(()=>{
this
.refresh();
})
}
},
components: {
Loading,
PullingWord
}
}
</script>