仿iPhone手机通讯录的滚动效果,代码参照了网络上各位大神。自己整理以做记录,也顺便巩固一下知识。使用到的组件如下: BetterSCROLL
一、基础滚动组件:scroll.vue
基于BetterScroll先封装一个基础的滚动组件,便于后续使用。
相关代码如下:
<template>
<div ref="rootRefDom">
<solt></solt>
</div>
</template>
<script>
import { ref } from 'vue'
import useScroll from './userScroll'
export default {
name: 'scroll',
props: {
probeType: {
type: Number,
default: 0
}
},
emits: ['scroll'],
setup(props, { emit }) {
const rootRefDom = ref(null)
useScroll(rootRefDom, props, emit)
return {
rootRefDom
}
}
}
</script>
部分代码注释:
ref="rootRefDom" 获取DOM结构;
probeType:设置滚动的灵明度,分别可以设置为1、2、3。数字越大,灵明度越高,当然性能也就越低。
emits: 在vue3需要通过这种方式来对外传递事件。
setup(),可以传入两个参数,一个是:props,一个是:context,context是一个对象,包含:{attrs, slots, emit, expose},此处只使用emit。后续通过emit将滚动的x、y坐标传出。
useScroll(): 分别将获取的DOM结构,props,emit作为参数传入。
二、 useScroll.js
页面挂载的时候实例化BScroll组件,通过ObserveDOM,监听页面数据填充后,实现滚动。页面离开的时候销毁之前的挂载。 传入的probeType大于设置的值时,监听滚动,通过传入的emit,调用外部的scroll事件,并将滚动的坐标值传出。
相关代码如下:
import { ref, onMonted, onUnMonted } from 'vue'
import BScroll from '@better-scroll/core'
import ObserveDOM from '@better-scroll/observe-dom'
BScroll.use(ObserveDOM)
export defautl function useScroll(rootRef, options, emit) {
const scroll = ref(null)
onMounted(() => {
const scrollValue = scroll.value = new BScroll(rootRef,{
observeDOM: true,
...options
})
if(options.probeType > 1) {
scrollValue.on('scroll', (pos) => {
emit('scroll', pos)
})
}
})
onUnMounted(() => {
scroll.value.destroy()
})
}
三、列表组件list.vue
页面按照iphone通讯录的样子,一个分类,对应一列分类数据,对应分类标题滚动到顶部后,自动吸在顶部。
此处数据直接在页面写死的,正常情况下都是从后端获取,这里写出来是表明数据结构。
相关代码如下:
<template>
<scroll class="list" :probeType="3" @scroll="onScroll">
<ul ref="groupRefDOM">
<li v-for="group in list" :key="group.title">
<h2>{{ group.title }}</h2>
<ul>
<li v-for="item in group.child" :key="item.name">
{{ item.name }}
</li>
</ul>
</li>
</ul>
<div class="fixedTop" v-if="fixTitle" :style="fixTitleStyle">{{fixTitle}}</div>
</scroll>
</template>
<script>
import scroll from './scroll'
import useFixTitle from './useFixTitle'
export default {
name: 'list',
components: {
scroll
},
setup() {
const list = [{title:'a',child:[{name: 'a1'}]},{title:'b',child:[{name: 'b1'}]}]
const fixTitleHeight = 30
const {groupRefDOM, onScroll, fixTitle, fixTitleStyle } = useFixTitle(list, fixTitleHeight)
return {
list,
groupRefDOM,
onScroll,
fixTitle,
fixTitleStyle
}
}
}
</script>
部分代码注释:
引入scroll组件,设置灵敏度最高3。监听组件的scroll事件。
ref="groupRefDOM" 获取滚动区域的DOM结构。
v-if="fixTitle" useFixTitle没有返回fixTitle时,不显示。
:style="fixTitleStyle" 标题滚动到顶部时,进行切换效果。
四、useFixTitle.js
第一部分
获取到页面结构,计算出各个分组的高度,并存入数组内,以便后续使用。
import { ref, reactive, watch, nextTick } from 'vue'
export default useFixTitle(list, fixTitleHeight) => {
const groupRefDOM = ref(null)
const groupHeightList = reactive([])
watch(list, async () => {
await nextTick()
clacGroupHeight()
})
function clacGroupHeight() {
const list = groupRefDOM.value.children
let height = 0
groupHeightList = []
groupHeightList.push(0)
for (let i = 0; i < list.length; i ++) {
height += list[i].clientHeight
groupHeightList.push(height)
}
}
return {
groupRefDOM,
onScroll
}
}
第二部分
响应页面滚动事件,实时计算滚动到哪个分类,并对页面上展示的分类内容进行替换。
import { ref, reactive, watch, nextTick, computed } from 'vue'
export default useFixTitle(list, fixTitleHeight) => {
...
const currentIndex = ref(0)
const ScrollY = ref(0)
const fixTitle = computed(() => {
if (ScrollY.value <= 0) {
return ''
}
const _current = currentIndex.value
return (groupHeightList.length != 0) ? list[_current].title : ''
})
function onScroll(pos) {
const ScrollYValue = Scroll.value = pos.y
for (let i = 0; i < groupHeightList.length - 1; i++) {
if (ScrollYValue > groupHeightList[i] && ScrollYValue < groupHeightList[i+1]) {
currentIndex.value = i
}
}
}
return {
...
onScroll,
fixTitle
}
}
第三部分
顶部两个分类交替时,出现动画效果
const distance = ref(0)
const fixTitleStyle = computed(() => {
const disValue = distance.value
const diff = (disValue > 0 && disValue < fixTitleHeight) ? disValue - fixTitleHeight
return {
transform: `translate3d(0, ${diff}px, 0)`
}
})
function onScroll(pos) {
const ScrollYValue = Scroll.value = - pos.y
for (let i = 0; i < groupHeightList.length - 1; i++) {
if (ScrollYValue > groupHeightList[i] && ScrollYValue < groupHeightList[i+1]) {
currentIndex.value = i
distance.value = groupHeightList[i+1] - ScrollYValue
}
}
}