前言
本文主要阐述用小程序的原生API如何实现一个IndexBar,当然本文使用的思路只是笔者想到的方法,如果有更好的方法欢迎在评论区指正。
简单实现
什么是IndexBar
我想大家在开发一定遇到过这种需求
左边是一个类似导航的东西,右边是一个轮动条,需要用户在点击左边导航的时候,右边自动滚动到指定位置,右边滚动条轮动的时候需要左右的导航自动选中标题。总体来说逻辑还是比较简单的。从上述需求也很容易得出实现这个东西总体需要两个核心逻辑
- 实现点击导航栏 滚动视图自动滚动到指定位置
- 实现滚动滚条 标题栏自动切换
实现视图根据标题自动滚动
在小程序中实现这个功能我们有两个方法
- 通过动态设置scroll-view的scroll-into-view 属性
- 通过获取scroll-view的context 执行如下方法
本项目使用第一种方法实现,第二种方式读者可以自己尝试一下 核心思路如下
- 在js代码中设置intoViewId 与 active 属性分别控制要跳转的scroll-view子视图和当前激活状态的index按钮 并创建changeActive方法用于实现按钮控制scroll-view
- 在wxml中绑定intoViewId 与设置changeActive事件
以下时核心代码
JS核心代码
js
Page({
/**
* 页面的初始数据
*/
data: {
// 用于绑定视图id
intoViewId:"",
// 用于设置当前处于激活状态的按钮
active:0,
categraylist:[{
name:"推荐",
},{
name:"拿铁",
},{
name:"美式",
},{
name:"摩卡",
}],
},
// 修改当前处于激活状态的导航 并 设置scroll需要进入viewId
changeActive(e){
const index = e.currentTarget.dataset.index
this.setData({
active:index
})
this.setData({
intoViewId:`id-${index}`
})
},
})
wxml代码
html
<view class="page">
<view class="top-bar">
</view>
<view class="list">
<view class="left-warpper">
<block wx:for="{{categraylist}}" wx:key="name">
<!--设置点击左边框事件 并把当前点击的索引传入 -->
<view data-index="{{index}}" bind:tap="changeActive" class="btn {{active == index ? 'active' :'' }}">{{item.name}}</view>
</block>
</view>
<!--scroll-into-view 绑定需要跳转的id scroll-with-animation 启用动画 -->
<scroll-view
class="coffee-warpper"
scroll-into-view="{{intoViewId}}"
scroll-with-animation
scroll-y>
<block wx:for="{{categraylist}}" wx:key="name">
<!-- 为scroll-view子元素设置id -->
<view id="id-{{index}}" class="coffee-container">
<view class="title">{{item.name}}</view>
<block wx:for="{{item.itemList}}" wx:for-item="itm" wx:key="name">
<coffee-item item="{{itm}}"></coffee-item>
</block>
</view>
</block>
<view class="blank"></view>
</scroll-view>
</view>
</view>
实现滚动滚动条 标题栏自动切换
实现这个功能 小程序并没有提供现成api 让我们可以绑定scroll-view上的事件就可以实现对scroll-view的监听虽然确实有一个绑定滚动监听的事件但是并没有满足我们需求,不过我们可以使用其他的API实现对scroll状态的监听 比如wx.createIntersectionObserver 这个API主要用来判断子元素是否进入父元素或者离开父元素,我们可以根据这个机制,实现对父元素中子元素的记录具体思路如下:
- 初始化一个Set保存子视图id
- 当事件触发时判断Set是否包含子视图id
- 如果不包含说明第一次触犯 说明是进入视图 把id加入Set
- 如果包含说明第二次触犯 说明是离开入视图 把id从Set中删除
- 遍历Set得到最小的id索引就是当前需要激活的侧边栏id索引
根据上面的思路我们可以只需要在js的代码中添加如下代码就可以实现需要的功能
js
// 用于保存当前在视图中视图id
let idSet = new Set()
Page({
onReady() {
this._observer = wx.createIntersectionObserver(this,{
observeAll:true
})
this._observer
.relativeTo('.coffee-warpper',{top:1})
.observe('.coffee-container', (res) => {
if(idSet.has(res.id)){
// 当前存在的这个VIEWID
// 说明scroll-view视图中存在这个子视图
// 再次触犯说明这个子视图 离开了scroll-view
idSet.delete(res.id)
let ids = Array.from(idSet).map(item=>{
return Number(item.replace("id-",""))
})
let min = Math.min(...ids)
this.setData({
active:min
})
}else{
idSet.add(res.id)
}
})
},
})