简单的实现过程
实现之前需要先明确一下需求:
- 弹性滚动
- 下拉刷新、上拉加载
- 自定义传入内容
- 事件派发
弹性滚动
实现弹性滚动只需要按照官方示例初始化即可实现。
VUE
代码语言:javascript复制<template>
<div class="wrapper" ref="wrapper">
<div class="content">
<div v-for="i in 100">{{ i }}</div>
</div>
</div>
</template>
<script setup lang="ts">
import BScroll from '@better-scroll/core'
import { onMounted } from 'vue'
let bscroll: BScroll
const wrapper = ref(null)
onMounted(() => {
bscroll = new BScroll(wrapper.value, {
mouseWheel: true
})
})
</script>
只需要在 mounted 阶段进行初始化创建对象即可。
下拉刷新、上拉加载
由于我安装的是 @better-scroll/core
,并没有安装‘全量包’ ,因此实现此功能同样需要安装其他两个插件:@better-scroll/pull-up
、@better-scroll/pull-down
安装后在初始化时开启选项即可。
TYPESCRIPT
代码语言:javascript复制import BScroll from '@better-scroll/core'
import Pullup from '@better-scroll/pull-up'
import Pulldown from '@better-scroll/pull-down'
BScroll.use(Pullup)
BScroll.use(Pulldown)
bscroll = new BScroll(wrapper.value, {
mouseWheel: true,
pullDownRefresh: true,
pullUpLoad: true
})
对于其触发事件的监听需要用实例对象去监听:
TYPESCRIPT
代码语言:javascript复制import BScroll from '@better-scroll/core'
import Pullup from '@better-scroll/pull-up'
import Pulldown from '@better-scroll/pull-down'
BScroll.use(Pullup)
BScroll.use(Pulldown)
bscroll = new BScroll(document.querySelector('.wrapper') as any, {
mouseWheel: true,
pullDownRefresh: true,
pullUpLoad: true
})
bscroll.on('pullingUp', () => {
console.log('触发了上拉')
})
bscroll.on('pullingUp', () => {
console.log('触发了下拉')
})
自定义传入内容
自定义传入内容这里,由于我并不想传入一个数据列表,然后去渲染元素。因此通过 watch
监听 props 传入的数据这个方法就不好用了;监听 slots
里的变化通过一番尝试,最终也是失败,因此最终选择了使用官方提供的插件:@better-scroll/observe-dom
、@better-scroll/observe-image
进行自动更新实例对象。安装完成后只需要在初始化前 use
此插件即可。
TYPESCRIPT
代码语言:javascript复制import BScroll from '@better-scroll/core'
BScroll.use(ObserveDOM)
BScroll.use(ObserveImage)
事件派发
需要派发的事件大致为:滚动事件和上拉下拉事件
- 滚动事件 此类事件只需要正常 emit 即可。
- 上拉下拉事件 上拉下拉刷新需要调用对应的结束事件才可以,因此将其封装为组件时就会遇到一个问题即需要等传入函数执行完毕后在调用结束事件。
TYPESCRIPT
代码语言:javascript复制import { defineProps, PropType, defineEmits } from 'vue'
const emit = defineEmits(['ckick', 'beforeScroll', 'afterScroll', 'scroll'])
const emit = defineEmits(['ckick', 'beforeScroll', 'afterScroll', 'scroll'])
const props = defineProps({
/**
* 是否派发滚动事件
*/
listenScroll: {
type: Boolean,
default: false
},
/**
* 是否派发滚动到底部的事件,用于上拉加载
*/
pullup: {
type: Function,
default: null
},
/**
* 是否派发顶部下拉的事件,用于下拉刷新
*/
pulldown: {
type: Function,
default: null
},
/**
* 是否派发列表滚动开始的事件
*/
beforeScroll: {
type: Boolean,
default: false
},
/**
* 是否派发列表滚动开始的事件
*/
afterScroll: {
type: Boolean,
default: false
}
})
/**
* 如果开启了滚动前事件派发
*/
if (props.beforeScroll) {
bscroll.on('beforeScrollStart', () => {
emit('beforeScroll')
})
}
/**
* 如果开启了滚动(滚动中)事件派发
*/
if (props.listenScroll) {
bscroll.on('scroll', (position: { x: number; y: number }) => {
emit('scroll', position)
})
}
/**
* 如果开启了滚动结束事件派发
*/
if (props.beforeScroll) {
bscroll.on('scrollEnd', () => {
emit('afterScroll')
})
}
if (props.pullup !== null) {
bscroll.on('pullingUp', () => {
try {
props.pullup().then(() => {
bscroll.finishPullUp()
})
} catch (e) {
// 传入非 Promise 函数
bscroll.finishPullUp()
}
})
}
if (props.pulldown !== null) {
bscroll.on('pullingDown', () => {
try {
props.pulldown().then(() => {
bscroll.finishPullDown()
})
} catch (e) {
// 传入非 Promise 函数
bscroll.finishPullDown()
}
})
}
完整代码
VUE
代码语言:javascript复制<template>
<div :class="{ wrapper: true, x: props.scrollX }" ref="wrapper">
<div class="content">
<slot></slot>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, defineProps, PropType, defineEmits } from 'vue'
import BScroll from '@better-scroll/core'
import ObserveDOM from '@better-scroll/observe-dom'
import ObserveImage from '@better-scroll/observe-image'
import Pullup from '@better-scroll/pull-up'
import Pulldown from '@better-scroll/pull-down'
let bscroll: BScroll
const wrapper = ref(null)
BScroll.use(ObserveDOM)
BScroll.use(ObserveImage)
BScroll.use(Pullup)
BScroll.use(Pulldown)
const emit = defineEmits(['ckick', 'beforeScroll', 'afterScroll', 'scroll'])
const props = defineProps({
/**
* 1 滚动的时候会派发scroll事件,会截流。
* 2 滚动的时候实时派发scroll事件,不会截流。
* 3 除了实时派发scroll事件,在swipe的情况下仍然能实时派发scroll事件
*/
probeType: {
type: Number as PropType<1 | 2 | 3>,
required: false,
default: 1
},
/**
* 是否开启横向滚动,默认纵向滚动。
* 开启横向滚动需要将传入元素设置为横向例如:display:inline-block
*/
scrollX: {
type: Boolean,
required: false,
default: false
},
/**
* 点击列表是否派发click事件
*/
click: {
type: Boolean,
default: true
},
/**
* 是否派发滚动事件
*/
listenScroll: {
type: Boolean,
default: false
},
/**
* 是否派发滚动到底部的事件,用于上拉加载
*/
pullup: {
type: Function,
default: null
},
/**
* 是否派发顶部下拉的事件,用于下拉刷新
*/
pulldown: {
type: Function,
default: null
},
/**
* 是否派发列表滚动开始的事件
*/
beforeScroll: {
type: Boolean,
default: false
},
/**
* 是否派发列表滚动开始的事件
*/
afterScroll: {
type: Boolean,
default: false
}
})
onMounted(() => {
bscroll = new BScroll(wrapper.value, {
scrollX: props.scrollX,
probeType: props.probeType,
click: props.click,
observeDOM: true,
observeImage: true,
mouseWheel: true,
pullDownRefresh: true,
pullUpLoad: true
})
/**
* 如果开启了滚动前事件派发
*/
if (props.beforeScroll) {
bscroll.on('beforeScrollStart', () => {
emit('beforeScroll')
})
}
/**
* 如果开启了滚动(滚动中)事件派发
*/
if (props.listenScroll) {
bscroll.on('scroll', (position: { x: number; y: number }) => {
emit('scroll', position)
})
}
/**
* 如果开启了滚动结束事件派发
*/
if (props.beforeScroll) {
bscroll.on('scrollEnd', () => {
emit('afterScroll')
})
}
if (props.pullup !== null) {
bscroll.on('pullingUp', () => {
try {
props.pullup().then(() => {
bscroll.finishPullUp()
})
} catch (e) {
// 传入非 Promise 函数
bscroll.finishPullUp()
}
})
}
if (props.pulldown !== null) {
bscroll.on('pullingDown', () => {
try {
props.pulldown().then(() => {
bscroll.finishPullDown()
})
} catch (e) {
// 传入非 Promise 函数
bscroll.finishPullDown()
}
})
}
})
</script>
<style scoped lang="scss">
@import './index.scss';
</style>
SCSS
代码语言:javascript复制.wrapper {
overflow: hidden;
.content {
display: block;
}
// 开启横向滚动
&.x {
white-space: nowrap;
.content {
display: inline-block;
}
}
}
外部使用时需要传入样式指定宽高
HTML
代码语言:javascript复制<scroll-view style="height: 300px; width: 300px">
<li v-for="i in count" :key="i">当前第{{ i }}个元素</li>
</scroll-view>