better-scroll实现左右两栏联动
首先对两栏进行基本样式的布局
给右侧字母栏,添加监听事件onShortcutTouchStart,首先传入一个事件参数,利用getData方法获取当前点击元素data-index的值
getData (el, name, val) {
let prefix = 'data-'
name = prefix + name
if (val) {
// 有值就设置属性
return el.setAttribute(name, val)
} else {
// 这里是获取属性值
return el.getAttribute(name)
}
}
e.target代表当前点击元素,点击右侧对应元素,左侧滚动到对应元素
// 点击右侧栏, 滚动的位置
onShortcutTouchStart (e) {
let anchorIndex = getData(e.target, 'index') // 获取当前点击元素的索引,这个索引是字符串
let firstTouch = e.touches[0] // 记录刚开始触碰的位置
this.touch.y1 = firstTouch.pageY // 记录y轴方向的位置
this.touch.anchorIndex = anchorIndex // 设置点击的索引,共享在两个函数之间
this._scrollTo(anchorIndex)// 传入当前元素的索引值,滚动到对应元素的位置
},
给右侧字母栏,添加监听事件onShortcutTouchMove事件,实现在右侧滑动,左侧依然跟着联动
// 在右边触摸移动的位置
onShortcutTouchMove (e) {
let firstTouch = e.touches[0] // 记录滑动后松手的位置
this.touch.y2 = firstTouch.pageY // 手指停止移动的位置
let delta = Math.floor((this.touch.y2 - this.touch.y1) / ANCHOR_HEIGHT) // 计算偏移了锚点
let anchorIndex = parseInt(this.touch.anchorIndex) + delta // 将获得的字符串索引,转化成数字 开始锚点加上偏移锚点
// console.log(delta)
this._scrollTo(anchorIndex)// 滚动到对应元素的位置
},
实现左侧滑动,右侧字母栏,对应字母高亮
首先定义,滑动距离scrollY和当前高亮字母索引currentIndex(默认为0)
然定义计算高度方法
// 计算左侧每组li对应的高度
_calculateHeight () {
this.listHeight = []
const list = this.$refs.listGroup // 拿到所有左侧每个字母li
let height = 0 // 刚开始高度为 0
this.listHeight.push(height) // 每个字母li对应的高度
for (var
let item = list[i]
height += item.clientHeight
this.listHeight.push(height)
}
}
}
}
在watch 里观测data值变化,调用计算高度的方法
观测scrllY的变化,在里面确定currentIndex的值,实现联动
scrollY (newY) {
const listHeight = this.listHeight
// 当滚动到顶部,newY>0
if (newY > 0) {
this.currentIndex = 0
}
// 在中键部分滚动(第一个元素是上线第二个元素的下限,类推所以i总的数目会比列表数要多一个,所以listHeight.length-1)
for (let i = 0; i < listHeight.length - 1; i++) {
let height1 = listHeight[i]
let height2 = listHeight[i + 1]
if (!height2 || (-newY >= height1 && -newY < height2)) {
this.currentIndex = i
return
}
console.log('索引' + this.currentIndex)
this.currentIndex = 0
}
// 当滚动到底部, 且-newY大于最后一个元素的上限
this.currentIndex = listHeight.length - 2
console.log('热门高亮')
}
//实现联动的关键是在初始化scroll组件的时候,派发一个scroll事件,并传递一个pos对象,通过监听scroll事件来改变scrollY的值
if (this.listenScroll) {
let me = this // 保存vue实例上下文
// 监听滚动事件
// console.log('监听滚动事件')
this.scroll.on('scroll', (pos) => {
console.log(pos)
me.$emit('scroll', pos) // 派发一个scroll事件,这里如果用this,默认指向scroll
})
}
总代码代码如下
<template>
<scroll class="listview" ref="listview" :listen-scroll = 'listenScroll' @scroll="scroll" :probe-type = 'probeType' :data="data">
<ul>
<!--每个分组热门、A,B....Z-->
<li v-for="(group, group_list) in data" :key="group_list" class="list-group" ref="listGroup">
<h2 class="list-group-title">{{group.title}}</h2>
<ul>
<!--每个字母分组对应的歌手-->
<li v-for="(item, index) in group.items" :key="index" class="list-group-item">
<img class="avatar" v-lazy="item.avatar">
<span class="name">{{item.name}}</span>
</li>
</ul>
</li>
</ul>
<div class="list-shortcut" @touchstart.stop.prevent="onShortcutTouchStart" @touchmove.stop.prevent="onShortcutTouchMove">
<ul>
<li v-for="(item, index) in shortcutlist" :class='{current: currentIndex === index}' :data-index="index" :key="index" class="item">
{{item}}
</li>
</ul>
</div>
</scroll>
</template>
<script>
import scroll from 'base/scroll/scroll'
import {getData} from 'common/js/dom'
const ANCHOR_HEIGHT = 18 // 这个高度是样式里每个li的高度
export default {
name: 'listview',
props: {
data: {
type: Array,
default () {
return []
}
}
},
created () {
this.touch = {} // 这个touch的为什么不在props里定义,原因是这个值不需要vue观测touch的变化
this.listenScroll = true
this.listHeight = []
this.probeType = 3
},
components: {
scroll
},
computed: {
// 右侧字母列表数组
shortcutlist () {
return this.data.map((group) => {
return group.title.substring(0, 1)
})
}
},
data () {
return {
scrollY: -1, // 观测实时滚动位置
currentIndex: 0 // 当前应该高亮的位置
}
},
watch: {
data () {
setTimeout(() => {
this._calculateHeight()
}, 20)
},
scrollY (newY) {
const listHeight = this.listHeight
for (let i = 0; i < listHeight.length; i++) {
let height1 = listHeight[i]
let height2 = listHeight[i + 1]
if (!height2 || (-newY > height1 && -newY < height2)) {
this.currentIndex = i
return
}
console.log('索引' + this.currentIndex)
this.currentIndex = 0
}
console.log('热门高亮')
}
},
methods: {
// 点击右侧栏, 滚动的位置
onShortcutTouchStart (e) {
let anchorIndex = getData(e.target, 'index') // 获取当前点击元素的索引,这个索引是字符串
let firstTouch = e.touches[0] // 记录刚开始触碰的位置
this.touch.y1 = firstTouch.pageY // 记录y轴方向的位置
this.touch.anchorIndex = anchorIndex // 设置点击的索引,共享在两个函数之间
this._scrollTo(anchorIndex)
},
// 在右边触摸移动的位置
onShortcutTouchMove (e) {
let firstTouch = e.touches[0] // 滑动后松手的位置
this.touch.y2 = firstTouch.pageY // 手指停止移动的位置y坐标
let delta = Math.floor((this.touch.y2 - this.touch.y1) / ANCHOR_HEIGHT) // 计算偏移了锚点
let anchorIndex = parseInt(this.touch.anchorIndex) + delta // 将获得的字符串索引,转化成数字 开始锚点加上偏移锚点
// console.log(delta)
this._scrollTo(anchorIndex)
},
// better-scroll会派发一个pos参数出来
scroll (pos) {
this.scrollY = pos.y // 滚动到的位置
},
// 滚动到当前点击元素的位置
_scrollTo (index) {
this.$refs.listview.scrollToElement(this.$refs.listGroup[index], 0)// scroll组件的滚动到对应元素的方法
},
// 计算左侧每组li对应的高度
_calculateHeight () {
this.listHeight = []
const list = this.$refs.listGroup // 拿到所有li
let height = 0 // 刚开始高度为 0
this.listHeight.push(height) // 第一个li对应的高度
for (var i = 0; i < list.length; i++) {
let item = list[i]
height += item.clientHeight
this.listHeight.push(height)
}
莫忘初心,负重前行。
}
</script>