垂直导航

mytrips.wxml加组件

scroll-into-view跳转到main-{{item.id}}view

1
2
3
4
5
6
7
8
9
10
<scroll-view scroll-y scroll-with-animation enable-back-to-top 
style="height:{{tripsHeight}}px"
scroll-into-view="{{scrollIntoView}}">
<view wx:for="{{trips}}" wx:key="id" class="trip" id="main-{{item.id}}">
<view class="action">
<text class="cuIcon-title text-green"></text>
{{trip.id}}
</view>
</view>
</scroll-view>

因为高度是变化的,使用下面函数来计算scroll-view的高度

1
2
3
4
5
6
7
8
onReady(){
wx.createSelectorQuery().select("#heading")
.boundingClientRect(rect =>{
this.setData({
tripsHeight: wx.getSystemInfoSync().windowHeight - rect.height
})
}).exec()
}

注意要加exec()不然不会运行

生成假数据演示

定义数据类型

1
2
3
4
5
6
7
8
interface Trip{
id: string
start: string
end: string
duration: string
fee: string
distance: string
}

在page data中添加trips

1
trips: [] as Trip[]

生成数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
populateTrips(){
const trips: Trip[] = []
for (let i = 0; i < 100 ; i++){
trips.push({
id: (10001+i).toString(),
start:'东方明珠'
end: '迪士尼'
distance: '22.0公里'
duration:'0时52分'
fee: '120.00元'
})
}
this.setData({
trips,
})
}

前端页面调试的小Tips

1
2
3
flex-direction: column;//排列方向
align-items: center;//
justify-content: space-between;

控制右侧内容的滚动

在左边使用swiper控件

navCount表示展示的数目,不同机型的高度是不一样的,这个值需要计算

1
2
3
4
5
6
7
8
9
10
11
<swiper duration="500" display-multiple-items="{{navCount}}" vertical
class="bg-white nav" style="height:{{tripsHeight}}px"
current-item-id="{{navScroll}}">
<swiper-item class="cu-item {{navSel===item.id?'text-green':''}}"
wx:for="{{navItems}}" wx:key="id" id="{{item.id}}"
item-id="{{item.id}}"
data-main-id="{{item.mainId}}" bindtap="onNavItemTap">
<view >{{item.label}}</view>
<view wx:if="{{navSel===item.id}}" class="cur"></view>
</swiper-item>
</swiper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.nav-container{
display: flex;
}
.nav-container swiper{
width: 152rpx;
}
.swiperItem{
display: flex;
align-items: center;
justify-content: center;
}
.cur{
height: 50%;
width: 8rpx;
border-radius: 10rpx 0 0 10rpx;
position: absolute;
background-color: currentColor;
top: 0;
right: 0rpx;
bottom: 0;
margin: auto;
}
.nav .cu-item{
text-align: center;
margin: unset;
padding: unset;
}

数据结构如下:

image-20220223105831662

1
2
3
4
5
6
7
8
9
10
11
12
//右边内容
interface MainItem{
id: string
navId: string
data: Trip
}
//左边导航
interface NavItem{
id: string
mainId: string
label: string
}

在data中加入

1
2
3
4
5
6
7
8
//右边数据
mainItems: [] as MainItem[],
//左边导航数据
navItems: [] as NavItem[],
//展现的数目
navCount: 0,
//但前所选择的导航
navSel:'',
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
populateTrips(){
const mainItems: MainItem[] = []
const navItems: NavItem[] = []
let navSel= ''
for (let i = 0; i < 100 ; i++){
mainItems.push({
id:'main-'+i,
navId:'nav-'+i,
data: {
id: (10001+i).toString(),
start:'东方明珠',
end: '迪士尼',
distance: '22.0公里',
duration:'0时52分',
fee: '120.00元',
status: '已完成',
},
})
navItems.push({
id: 'nav-'+i,
mainId: 'main-'+i,
label: (10001+i).toString(),
})
if (i===0){
navSel = 'nav-'+i
}
}
this.setData({
mainItems,
navItems,
navSel,
})
},

开发中遇到一个小问题,bindtap的点击函数无法激活,原因:前端的标签没有指定高度和宽度。

控制左侧导航滚动

1
2
3
4
5
6
7
8
9
10
11
12
13
interface MainItem{
id: string
navId: string
navScrollId: string
data: Trip
}
interface MainItemQueryResult{
id: string
top: number
dataset:{
navId: string
navScrollId: string
}

data中加入

1
2
3
4
5
scrollStates: {
mainItems: [] as MainItemQueryResult[],
},
//左侧第一个nav
navScroll: '',
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
populateTrips(){
const mainItems: MainItem[] = []
const navItems: NavItem[] = []
let navSel= ''
// 上一个nav,也是滚动的标签
let prevNav = ''
for (let i = 0; i < 100 ; i++){
if (!prevNav) {
prevNav = 'nav-'+i
}
mainItems.push({
id:'main-'+i,
navId:'nav-'+i,
navScrollId: prevNav,
data: {
id: (10001+i).toString(),
start:'东方明珠',
end: '迪士尼',
distance: '22.0公里',
duration:'0时52分',
fee: '120.00元',
status: '已完成',
},
})
navItems.push({
id: 'nav-'+i,
mainId: 'main-'+i,
label: (10001+i).toString(),
})
if (i===0){
navSel = 'nav-'+i
}
prevNav = 'nav-'+i
}
this.setData({
mainItems,
navItems,
navSel,
},() =>{
this.prepareScrollstatus()
})
},
prepareScrollstatus(){
wx.createSelectorQuery().selectAll('.main_items').fields({
id: true,
dataset:true,
rect: true,
}).exec(res =>{
console.log(res)
this.scrollStates.mainItems = res[0]
})
},

这里的data-nav-scroll-id是第一nav的Id,审美上,当前的nav在第一个并不好看。这里选择的是第二个,所以滚动的实际位置是第一个,高亮的是下一个。data-nav-scroll-id表示的就是高亮的上一个nav

image-20220223164632712

1
2
3
4
5
6
<scroll-view scroll-y scroll-with-animation enable-back-to-top 
style="height:{{tripsHeight}}px"
scroll-into-view="{{mainScroll}}"
bindscroll="onMainScroll">
<view wx:for="{{mainItems}}" wx:key="id" class="padding-bottom padding-lr main_items"
id="{{item.id}}" data-nav-id="{{item.navId}}" data-nav-scroll-id="{{item.navScrollId}}">

滚动事件函数

思路:计算当前滚动的像素和本身的元素像素的大小比较。

scrollStates表示每个item的属性,包含了每个item的像素位置,通过prepareScrollstatus()函数获取。

计算当前滚动到的像素值和scrollStates中每个item的像素位置比较,找到第一个scrollStates中的item的高度大于当前的像素高度,就表示当前item是第一个完全展现在屏幕中的item。

找到这个item之后,我们设置左边的滚动为上一个nav。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
onMainScroll(e: any){
console.log(e)

const top: number = e.currentTarget?.offsetTop + e.detail?.scrollTop
if (top === undefined){
return
}
//find 函数表示遍历返回第一个满足函数的值
const selItem = this.scrollStates.mainItems.find(v => v.top>=top)
if (selItem === undefined){
return
}
this.setData({
navSel:selItem.dataset.navId,
navScroll:selItem.dataset.navScrollId,
})
},