这几天整索引列表,uniapp原生的索引列表太烂了,不太满足需求,所以找了个uv-index-list的索引列表来用,但是翻了翻组件示例发现没有返回顶部的功能,偏偏还得用到,就只能自己动手改了,这里记一下,说不定以后有类似的情况可以用上。
翻到uv-index-list的组件vue文件uv-index-list.vue,可以找到创建索引的view对应的class是uv-index-list__letter__item,照抄一份,去掉v-for的循环,里面的item换成向上箭头↑
<view
class="uv-index-list__letter"
ref="uv-index-list__letter"
:style="{ top: $uv.addUnit(letterInfo.top || 100 ,'px') }"
@touchstart="touchStart"
@touchmove.stop.prevent="touchMove"
@touchend.stop.prevent="touchEnd"
@touchcancel.stop.prevent="touchEnd"
>
<!-- 照抄一个创建索引的view -->
<view
class="uv-index-list__letter__item"
:style="{
backgroundColor: activeIndex === -1 ? activeColor : 'transparent'
}"
>
<text
class="uv-index-list__letter__item__index"
:style="{color: activeIndex === -1 ? '#fff' : inactiveColor}"
>↑</text>
</view>
<view
class="uv-index-list__letter__item"
v-for="(item, index) in uIndexList"
:key="index"
:style="{
backgroundColor: activeIndex === index ? activeColor : 'transparent'
}"
>
<text
class="uv-index-list__letter__item__index"
:style="{color: activeIndex === index ? '#fff' : inactiveColor}"
>{{ item }}</text>
</view>
</view>
这里可以看到点击索引的时候会触发touchStart方法,顺着这个方法找,会看到touchStart的代码
// 索引列表被触摸
touchStart(e) {
// 获取触摸点信息
const touchStart = e.changedTouches[0]
if (!touchStart || this.disTap) return
this.touching = true
const {
pageY
} = touchStart
// 根据当前触摸点的坐标,获取当前触摸的为第几个字母
const currentIndex = this.getIndexListLetter(pageY)
this.setValueForTouch(currentIndex)
},
发现这里用getIndexListLetter获取了触摸点的坐标,然后用下面的setValueForTouch方法执行跳转事件,获取坐标的方法不用管,直接看到setValueForTouch,会看到里面是根据getIndexListLetter获取的触摸点对应的索引index执行了跳转,刚才已经在页面上增加了一个↑的索引了,所以这里要减去↑的偏移量,并且设置↑对应滚动的id:uv-index-item-header
// 设置各项由触摸而导致变化的值
setValueForTouch(currentIndex) {
// 如果是0的情况表示是向上箭头,直接回到顶部
if(currentIndex == 0){
// 设置当前激活的是表头
this.activeIndex = -1;
// 根据id返回到表头所在的位置
this.scrollIntoView = 'uv-index-item-header'
return
}
// 如果偏移量太小,前后得出的会是同一个索引字母,为了防抖,进行返回
if (currentIndex - 1 === this.activeIndex) return
// 偏移量减去返回顶部箭头
this.activeIndex = currentIndex - 1
// #ifndef APP-NVUE || MP-WEIXIN
// 在非nvue中,由于anchor和item都在uv-index-item中,所以需要对index-item进行偏移
this.scrollIntoView = `uv-index-item-${this.uIndexList[this.activeIndex].charCodeAt(0)}`
// #endif
// #ifdef MP-WEIXIN
// 微信小程序下,scroll-view的scroll-into-view属性无法对slot中的内容的id生效,只能通过设置scrollTop的形式去移动滚动条
this.scrollTop = this.children[this.activeIndex].top
// #endif
// #ifdef APP-NVUE
// 在nvue中,由于cell和header为同级元素,所以实际是需要对header(anchor)进行偏移
const anchor = `uv-index-anchor-${this.uIndexList[this.activeIndex]}`
dom.scrollToElement(this.anchors[this.activeIndex].$refs[anchor], {
offset: 0,
animated: false
})
// #endif
},
设置到这里的时候,调试会发现点索引会产生偏移,这是因为初始化组件的时候没有把↑的位置考虑上,翻到mounted钩子,看到用到了setIndexListLetterInfo设置索引和坐标的关系,所以这里加上↑的高度来处理
// 设置indexList索引的尺寸信息
setIndexListLetterInfo() {
this.getIndexListLetterRect().then(size => {
const {
top,
height
} = size
const windowHeight = this.$uv.sys().windowHeight;
let customNavHeight = 0
// 消除各端导航栏非原生和原生导致的差异,让索引列表字母对屏幕垂直居中
if (this.customNavHeight == 0) {
// #ifdef H5
customNavHeight = this.$uv.sys().windowTop
// #endif
// #ifndef H5
// 在非H5中,为原生导航栏,其高度不算在windowHeight内,这里设置为负值,后面相加时变成减去其高度的一半
customNavHeight = 0
// #endif
} else {
customNavHeight = this.$uv.getPx(this.customNavHeight)
}
this.letterInfo = {
height,
// 为了让字母列表对屏幕绝对居中,让其对导航栏进行修正,也即往上偏移导航栏的一半高度
top: (windowHeight - height) / 2 + customNavHeight / 2,
// 初始化itemHeight的时候加上返回表头符号的高度
itemHeight: Math.floor(height / (this.uIndexList.length + 1))
}
})
},
到这个时候,基本点击其他索引都没啥问题了,但是点击返回顶部的箭头没有反应,这里就用上了刚才加的↑元素对应的id:uv-index-item-header,把id给插槽header加上
<scroll-view
:scrollTop="scrollTop"
:scrollIntoView="scrollIntoView"
:offset-accuracy="1"
:style="{
maxHeight: $uv.addUnit(scrollViewHeight,'px')
}"
scroll-y
@scroll="scrollHandler"
ref="uvList"
>
<!-- header的view加上跳转对应的id -->
<view id='uv-index-item-header'>
<slot name="header" />
</view>
<slot />
<view>
<slot name="footer" />
</view>
</scroll-view>
到这里,基本跳转就都OK了,最后把点击索引显示的气泡加上
<uv-transition
mode="fade"
:show="touching"
:customStyle="{
position: 'fixed',
right: '40px',
top: $uv.addUnit(indicatorTop,'px'),
zIndex: 2
}"
>
<view class="uv-index-list__indicator__box">
<view
class="uv-index-list__indicator"
:class="['uv-index-list__indicator--show']"
:style="{
height: $uv.addUnit(indicatorHeight,'px'),
width: $uv.addUnit(indicatorHeight,'px')
}"
>
<!-- activeIndex == -1 的情况表示返回表头 -->
<text class="uv-index-list__indicator__text">{{ activeIndex == -1?'↑':uIndexList[activeIndex] }}</text>
</view>
</view>
</uv-transition>
大功告成。
这里面其他用到的控制页面高度,索引栏的位置的其他方法,基本都不影响点击索引跳转,也就没必要修改了,另外这个调整只适用于uniapp的H5环境,且只适用于点击索引栏跳转,其他后续触发动作比如回传currentIndex的部分是没有改动的,这个请注意。
理论上这个插件是支持nvue和微信小程序的,不过我只用到H5的,调试也只调试H5的,所以不确定其他环境能不能使,大致也能当个参考。
补充一点内容
uv-index-item初始化的时候,class的名字会从A开始取charCodeAt,导致点击索引栏的时候跳转错位,去掉uv-index-item里面初始化调用init(),改为外部传入porps的charCode可以规避这个问题
<template>
<!-- #ifdef APP-NVUE -->
<cell ref="uv-index-item">
<!-- #endif -->
<view
class="uv-index-item"
:id="`uv-index-item-${charCode}`"
:class="[`uv-index-item-${charCode}`]"
>
<slot />
</view>
<!-- #ifdef APP-NVUE -->
</cell>
<!-- #endif -->
</template>
props:{
// 不使用原来的自动获取id的方式,会从A开始取charcode,导致id错位
charCode:{
type:Number,
default:null
}
},