涉及到一些TARO,但是不多,类似微信小程序。
方法一:
前置条件:
1、每个字母下的城市列表使用一个容器包着,容器带有id,方便后续锚点跳转。
2、navigatorBar里的字母高定死,知道navigatorBa总高度,方便知道滑动到哪个字母。
方法:
锚点跳转
监听navigatorBar的滑动距离,计算出所滑动的点是在哪个字母的范围(比如我知道一共有10个字母,一个字母定高10px,我滑动了30px,30px在第三个字母的范围内,所以我就知道了我滑到了第三个字母。)取出这个字母,使用scroll View的锚点跳转。
ScrollView代码:主要是列表使用容器,容器id为字母
// ScrollView左边城市列表
<ScrollView
scrollY
className="scroll-view"
scrollTop={scrollToY}
style={{ height: `${isH5 ? `${phoneHeight - 100}px` : `${phoneHeight - headTop - 100}px`}` }}
>
{
Object.keys(cityList).length
? <>
{
Object.keys(cityList).map((letter) => <View
id={letter} // 使用字母作为id,方便锚点跳转
key={letter}
className="letter-container"
>
<View key={letter} className="city-list-letter">{letter}</View>
{
cityList[letter].map((item) => <View
className="city-list-name"
onClick={selectCity}
data-city={item}
key={item.cityCode}
>{item.cityName}</View>)
}
</View>)
}
</>
: <SelectCitySkeleton />
}
</ScrollView>
navigation-bar代码:主要是监听滑动距离、以及使用data-key,方便后续取字母
// 右边navigation-bar
{
Object.keys(cityList).length
? <>
<View
className="navigation-bar"
onTouchMove={handleTouchMove} // 监听滑动距离
style={{ top: `${isH5 ? 0 : `${headTop + 100}px`}` }}
>
<View className="bar-item" data-key="hotCity" onClick={clickBar}>#</View>
{
Object.keys(cityList).map((item, index) => <View
key={index}
data-key={item}
onClick={clickBar}
className="bar-item"
>
{item}
</View>)
}
</View> </>
: skeletonLetter() // 骨架屏
}
处理函数代码:
// 滑动bar
const handleTouchMove = (e) => {
const barHeight = (40 * (Object.keys(cityList).length + 1)) / 2 // bar的高度
const touchClientY = e.changedTouches[0].clientY // 自顶滑动距离
const touchDistance = touchClientY - headTop - 100 // bar内滑动距离
const letterArr = Object.keys(cityList) // 字母列表
let currentLetter = 'hotCity' // 默认热门城市
let toastText = '热门城市'
let touchBarItemIndex = -1 // 默认#号
if (touchDistance >= 0) {
touchBarItemIndex = Math.floor(touchDistance / 20) - 1
}
// 滑动距离超过bar的高度
if (touchDistance >= barHeight) {
touchBarItemIndex = letterArr.length - 1 // 定位到最后一位
}
// 锚点跳转
if (touchBarItemIndex === -1) {
setLetter('hotCity')
scrollIntoView('hot-city') // 锚点跳转
} else {
const letter = letterArr[touchBarItemIndex]
setLetter(letter)
toastText = letter
scrollIntoView(letter) // 锚点跳转
}
if (letter !== currentLetter) {
Taro.vibrateShort() // 短暂震动
Taro.showToast({
title: `${toastText}`,
icon: 'none',
duration: 500,
})
}
}
思想二:
前置条件:
1、城市列表中的每个城市Item也需要定死高,为方便使用srollView的setScrollToY功能跳转,
2、拿到城市列表数据处理时需计算好高度以及需要滑动的高度。(后面讲处理的方法
方法:
和前面一样,监听navigatorBar的滑动距离,计算出所滑动的点是在哪个字母的范围(比如我知道一共有10个字母,一个字母定高10px,我滑动了30px,30px在第三个字母的范围内,所以我就知道了我滑到了第三个字母。)计算好跳哪个字母后,直接拿这个字母里的数据处理好的里的Y。
处理数据
数据处理大概就是,
高度:知道一个字母里有多少个城市,然后乘一个城市Item的高,算到这个字母里所有城市的总高。
需要滑动的距离:一个字母需要滚动的距离就是它前几个字母的高相加。
1、首先需要定义一个最外层变量,用于存储字母的累加置,即下个字母需要滚动的高度
let lastItemScrollHeight = 0
2、计算一个字母以及它下面城市的总高度
/*一个字母容器的高**/
formatList[item].height = Object.keys(formatList[item]).length * 42 + 30 // 字母容器高度
比如B字母下有10个城市,每个城市高为40px,字母高30px那他的高就是 10*40+30px
计算这个字母需要滚动的距离
3、由于我们已经定义了一个外层变量,在每处理完一个字母容器的高度后,就把高度累加到这个变量上,作为下一个字母需要滚动距离。
/*这个字母需要滚动的距离**/
Object.keys(formatList).forEach((item) => {
formatList[item].scrollHeight = lastItemScrollHeight
lastItemScrollHeight += formatList[item].height // 距离顶部高度
})
假如我们已经算到A和B字母的高度,现在需要知道我滑动带C字母需要滑动多少距离,那就是需要滑动A+B字母高度加起来的高度
数据处理后长这样。
最后的处理函数:
// 滑动bar
const handleTouchMove = (e) => {
const barHeight = (40 * (Object.keys(cityList).length + 1)) / 2 // bar的高度
const touchClientY = e.changedTouches[0].clientY // 自顶滑动距离
const touchDistance = touchClientY - headTop - 100 // bar内滑动距离
const letterArr = Object.keys(cityList)
let currentY = 0 // 默认热门城市
let toastText = '热门城市'
let touchBarItemIndex = -1 // 默认#号
if (touchDistance >= 0) {
touchBarItemIndex = Math.floor(touchDistance / 20) - 1
}
// 滑动距离超过bar的高度
if (touchDistance >= barHeight) {
touchBarItemIndex = letterArr.length - 1 // 定位到最后一位
}
// 计算需要scroll的距离
if (touchBarItemIndex === -1) {
currentY = 0
} else {
const letter = letterArr[touchBarItemIndex]
toastText = letter
currentY = cityList[letter].scrollHeight + 127
}
if (scrollToY !== currentY) {
setScrollToY(currentY)
Taro.vibrateShort()
Taro.showToast({
title: `${toastText}`,
icon: 'none',
duration: 500,
})
}
}