小程序实现详情页滚动渐变导航条

背景

由于项目需要,需要在我们的详情页做一个类似京东详情页的滚动渐进增强的自定义导航条。接下来就简单记录下开发时的实现思路,以及自己实现中的问题,方便以后查阅。

前期准备

由于微信的限制,我们要实现单一页面的自定义导航条需要对这个页面进行配置才允许自定义。如果是所有页面都要自定义导航条,那就需要在 app.json 中全局配置了。具体配置如下:

1
"navigationStyle": "custom",

实现思路

首先我们要实现自定义导航条,那么我们就需要获得导航条的宽高和边距大小。用一个普通的盒子来模拟这导航条的样子,然后盒子的内部就可以放一些我们想放的元素和切换展示的 icon。

获取导航条的属性

获取导航条的宽高需要借助微信的两个 api:wx.getSystemInfo()wx.getMenuButtonBoundingClientRect(),具体 api 描述请看微信官方文档。因为自定义导航条可能会在多个页面使用这个数据变量。故把获取数据的计算过程放在 app.js 中。计算的结果数据也存储到 app.js 的全局变量中,方便以后其它页面的取用。具体代码如下:

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
/**
* 设置导航的高度
*/
setNavBarHeight () {
// 默认高度
this.globalData.navHeight = 88
// 默认距离上方的距离
this.globalData.navTop = 50
// 默认状态栏的高度
this.globalData.statusBarHeight = 44
try {
// 获取菜单按钮(右上角胶囊按钮)的布局位置信息。坐标信息以屏幕左上角为原点。
const menuButtonObject = wx.getMenuButtonBoundingClientRect()
// 获取系统信息
wx.getSystemInfo({
success: res => {
// 解构出所需要的计算数据
const { top = 50, height = 32 } = menuButtonObject || {top: 50, height: 32}
const { statusBarHeight = 44, system,safeArea } = res
/**
* 通过右上角的胶囊按钮计算出导航条的高度
*
**/
const navHeight = statusBarHeight + height + (top - statusBarHeight) * 2
this.globalData.navHeight = navHeight // 导航高度
this.globalData.navTop = top // 胶囊按钮与顶部的距离
this.globalData.statusBarHeight = statusBarHeight // 状态栏高度
this.globalData.system = system
this.globalData.safeAreaTop = safeArea.top
},
fail: () => {
this.globalData.navHeight = 88
this.globalData.navTop = 50
this.globalData.statusBarHeight = 44
}
})
} catch (err) {
this.globalData.navHeight = 88
this.globalData.navTop = 50
this.globalData.statusBarHeight = 44
}
}
})

通过以上代码实现,已经获取到了导航条的高度、状态栏的高度等信息,以供后面使用

封装自定义导航条组件

对于封装成单独的一个组件,很显然是为了更大的复用。以后如果在哪个页面再需要自定义导航条的话,直接修改下导航条的配置和引入使用下组件就好了,详细组件封装如下:

  • navbar/index.wxml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<view class='navbar' style='height:{{navH}}px;background-color:rgba(51,51,51,{{bgOpacityValue}});'>
<view
wx:if='{{showNavIcon}}'
class="navbar-action-wrap navbar-action-group {{!showBgColor&&'navbar-navback-bg'}}"
style='top:{{navTop}}px;'
bind:tap="{{showBackHome?'toIndex':'navBack'}}">
// 是否显示返回主页的小房子图标
<view wx:if='{{showBackHome}}' class="iconfont icon-nav_back_home icon-me {{!showBgColor&&'icon-hidden'}}" />
<view wx:if='{{!showBackHome}}' class="iconfont icon-nav_back icon-me {{!showBgColor&&'icon-hidden'}}" />
</view>
// 导航条中的两个tab按钮切换显示内容
<view wx:if='{{bgOpacityValue}}' class='navbar-title {{!showBgColor&&"navbar-title-hidden"}}' style='top:{{navTop}}px'>
<text class="navbar-play-other {{currentIndex===0&&'navbar-tab-active'}}" data-value='{{0}}' bind:tap='selectTitle'>
商品
</text>
<text wx:if='{{showPlayTab}}' class="navbar-play-other {{currentIndex===1&&'navbar-tab-active'}}" data-value='{{1}}' bind:tap='selectTitle'>
评价
</text>
</view>
</view>
  • navbar/index.js
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
const App = getApp()

Component({
options: {
addGlobalClass: true,
},
// 组件的属性列表
properties: {
// 返回icon的颜色
showIconBgColor: {
type: Boolean,
value: true,
},
// 是否显示导航条icon
showNavIcon: {
type: Boolean,
value: true,
},
// 渐进色值(控制头部滚动变色)
bgOpacityValue: {
type: Number,
value: 0,
},
// 当前的tab值
currentIndex: {
type: Number,
value: 0,
},
// 是否显示返回首页按钮判断值
showBackHome: {
type: Boolean,
value: false,
},
// 是否显示tab判断值
showPlayTab: {
type: Boolean,
value: true,
},
// 标题一字段
playTitle: {
type: String,
value: '',
},
// 标题2字段
goodsTitle: {
type: String,
value: '',
},
},

// 组件的初始数据
data: {
titleIndex: 0, // 默认高量的tile(比如商品)
navH: App.globalData.navHeight || 88, // 导航条高度
navTop: App.globalData.navTop || 50, // 距离顶部高度
statusBarHeight: App.globalData.statusBarHeight || 44, // 状态栏高度
},

// 组件的方法列表
methods: {
// 回退
navBack() {
wx.navigateBack()
},
// 回主页
toIndex() {
wx.reLaunch('home')
},
// 返回数据判断是商品还是评价
selectTitle(e) {
this.setData({
titleIndex: e.currentTarget.dataset.value,
})
this.triggerEvent('selectTitle', e.currentTarget.dataset.value)
},
},
})
  • navbar/index.wxss
    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
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    .navbar {
    width: 100%;
    overflow: hidden;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 999999;
    flex-shrink: 0;
    }
    .navbar-title {
    box-sizing: border-box;
    padding-left: 119rpx;
    height: 32px;
    line-height: 32px;
    text-align: center;
    position: absolute;
    left: 0;
    z-index: 10;
    color: #fff;
    font-size: 13px;
    font-weight: bold;
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
    display: table-cell;
    vertical-align: bottom;
    opacity: 1;
    }
    .navbar-title-hidden {
    opacity: 0;
    }
    .navbar-play-other {
    margin-left: 40rpx;
    font-weight: 400;
    }
    .navbar-tab-active {
    font-size: 34rpx;
    color: #fdd100;
    font-weight: bold;
    }
    .navbar-action-wrap {
    display: -webkit-flex;
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    left: 10px;
    z-index: 11;
    }
    .navbar-navback-bg {
    background-color: rgba(255, 255, 255, 0.56);
    border: 1px solid rgba(0, 0, 0, 0.08);
    }
    .navbar-action-group {
    border-radius: 50%;
    overflow: hidden;
    height: 64rpx;
    width: 64rpx;
    }

    .navbar-action_item {
    padding: 3px 0;
    color: #333;
    }

    .navbar-action-group .navbar-action_item {
    border-right: 1px solid #f0f0f0;
    padding: 3px 14px;
    }

    .navbar-action-group .last {
    border-right: none;
    }
    .icon-me {
    color: #fff;
    }
    .icon-hidden {
    color: #333;
    }

至此一个自定义导航条就封装完成了,接下来就是在目标页面使用此组件就好了。

使用导航条组件

先在使用页面的配置文件中引入组件

1
2
3
"usingComponents": {
"navbar": "/components/navBar/index",
},

在.wxml文件中进行使用

1
<navbar bind:selectTitle="selectTitle" goodsTitle="{{goodsDetail}}" playTitle="{{playTitle}}" currentIndex="{{current}}" showBgColor="{{current===1||isBackShow||showNavTitle}}" bgOpacityValue="{{(current===1&&1)||opacityValue}}" showBackHome="{{showBackHome}}" showPlayTab="{{ isShowPlayTab }}"></navbar>

然后在js中写相应的逻辑。以上是组件的封装,然后就是对用户往下滚动的时候导航条的颜色渐变加深,往上滚动的时候颜色渐变消失。主要的实现思路是通过透明度实现的。具体如下:

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// 滚动实现渐变色
handleScroll (e) {
const { scrollTop } = e.detail
if (this.versionCompare(wx.getSystemInfoSync().SDKVersion, '2.5.2') >= 0) {
if (this.data.current === 0) {
if (scrollTop > 6 && scrollTop < 100) {
this.setData({
isBackShow: true,
opacityValue: scrollTop / 100
})
if (scrollTop > 60) {
wx.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: '',
animation: {
duration: 400,
timingFunc: 'easeIn'
}
})
} else {
wx.setNavigationBarColor({
frontColor: '#000000',
backgroundColor: '',
animation: {
duration: 400,
timingFunc: 'easeIn'
}
})
}
} else if (scrollTop > 100) {
this.setData({
isBackShow: true,
opacityValue: 1
})
wx.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: '',
animation: {
duration: 400,
timingFunc: 'easeIn'
}
})
} else {
this.setData({
isBackShow: false,
opacityValue: 0
})
wx.setNavigationBarColor({
frontColor: '#000000',
backgroundColor: '',

animation: {
duration: 400,
timingFunc: 'easeIn'
}
})
}
} else {
if (scrollTop > 6) {
this.setData({
isBackShow: true
})
} else {
this.setData({
isBackShow: false
})
}
}
} else {
wx.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: '#333333',
animation: {
duration: 400,
timingFunc: 'easeIn'
}
})
}
},

小结

各位看官,一次自定义导航踩坑已经结束,对于自己来说也是一次成长。如果对于你也有帮助的还请给个赞,谢谢大家。写的不好,如有问题请指正。

-------------本文结束感谢您的阅读-------------

本文标题:小程序实现详情页滚动渐变导航条

文章作者:Water

发布时间:2020年08月27日 - 18:08

最后更新:2023年08月01日 - 06:08

原始链接:https://water.buging.cn/2020/08/27/小程序实现京东详情页实现滚动渐变导航条/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!