├── README.md
├── _config.yml
├── app.js
├── app.json
├── app.wxss
├── components
└── nav-dynamic
│ ├── icon_home_black.png
│ ├── icon_home_white.png
│ ├── nav-dynamic.js
│ ├── nav-dynamic.json
│ ├── nav-dynamic.less
│ ├── nav-dynamic.wxml
│ └── nav-dynamic.wxss
├── pages
└── index
│ ├── index.js
│ ├── index.json
│ ├── index.wxml
│ └── index.wxss
├── project.config.json
└── sitemap.json
/README.md:
--------------------------------------------------------------------------------
1 | # nav-dynamic
2 | 微信小程序自定义nav头部组件;适配全面屏设计;
3 |
4 | ## 实现功能
5 | 1. 初始进入页面时,展示初始状态下的nav样式;
6 | 2. 页面滚动时,监听页面滚动事件,展示滚动状态下的nav样式;
7 | 3. 根据配置字段值、页面栈数量,展示“返回”图标;
8 | 4. 根据配置字段值、页面栈数量,展示“首页”图标,同时配置“首页路径”;
9 | 5. 设置组件插槽,允许开发者在组件上添加任意元素;
10 |
11 | ## 方法说明
12 | * getNavHeight()
13 | #### 获取导航栏高度;单位px;
14 |
15 | * setOptions(options)
16 | #### 设置组件参数;
17 | options Object
18 |
19 | | 参数名称 | 类型 | 默认值 | 说明 | 备注 |
20 | | :----: | :----: | :----: | :----: | :----: |
21 | | navBackgroundInit | String | '#ffffff' | 导航栏背景颜色(初始值) | 当nav要设置透明时,可设置成'transparent' |
22 | | navBackgroundRoll | String | '#000000' | 导航栏背景颜色(滚动值) | 当nav要设置透明时,可设置成'transparent' |
23 | | titleColorInit | String | '#ffffff' | 文本颜色(初始值) | 只能设置成16进制,不可简写 |
24 | | titleColorRoll | String | '#000000' | 文本颜色(滚动值) | 只能设置成16进制,不可简写 |
25 | | titleTextInit | String | '' | 标题文本(初始值) | 无 |
26 | | titleTextRoll | String | '' | 标题文本(滚动值) | 无 |
27 | | historyShow | Boolean | true | 历史图标是否显示 | 值为false,隐藏图标;值为true,当页面栈数量小于2时,隐藏图标,否则,显示图标 |
28 | | scrollMin | Number | 50 | 最小滚动间距 | 当页面滚动距离小于scrollMin时;组件的opacity值为0 |
29 | | scrollMax | Number | 200 | 最大滚动间距 | 当页面滚动距离大于scrollMax时;组件的opacity值为1 |
30 | | homeShow | Boolean | false | home图标是否显示 | 值为false,隐藏图标;值为true,还要设置homeJudgeStack再行判断 |
31 | | homeJudgeStack | Boolean | true | home图标显示是否判断页面栈 | 值为false,显示图标;值为true,当页面栈数量小于2时,显示图标,否则,隐藏图标(homeShow值为true才有意义) |
32 | | homePath | String | '/pages/index/index' | home页面路径 | 无 |
33 | | homeColorInit | String | 'white' | home图标颜色(初始值) | white / black |
34 | | homeColorRoll | String | 'black' | home图标颜色(初始值) | white / black |
35 |
36 | * scrollHandle(scrollTop)
37 | #### 页面滚动事件回调;调用这个方法可实现nav组件动态改变样式
38 |
39 | | 参数名称 | 类型 | 默认值 | 说明 | 备注 |
40 | | :----: | :----: | :----: | :----: | :----: |
41 | | scrollTop | Number | 0 | 页面滚动距离 | 无 |
42 |
43 | ## 插槽用法
44 | #### 插槽名称
45 | ###### ant-nav-slot
46 |
47 | #### 插槽用法
48 | ```
49 |
50 | 我是插槽
51 |
52 | ```
53 |
54 | ## 使用栗子
55 | #### 1、在app.json中全局配置组件
56 | ```
57 | "usingComponents": {
58 | "comp-nav-dynamic": "/components/nav-dynamic/nav-dynamic"
59 | },
60 | ```
61 | #### 2、在页面的wxml中引入组件
62 | ```
63 |
64 | ```
65 | #### 3、在页面的js中使用 this.selectComponent('#comp-nav-dynamic') 方法获取组件实例,并调用setOptions方法配置参数;然后在页面的onLoad生命周期中调用;当页面有滚动修改nav组件样式需求时,在页面的onPageScroll的页面方法中,调用实例的scrollHandle方法;
66 | ```
67 | Page({
68 | data: {
69 | navHeight: 0,
70 | },
71 | onLoad() {
72 | this.setNav();
73 | },
74 | setNav() {
75 | this.selectComponent('#comp-nav-dynamic').setOptions({
76 | navBackgroundInit: '#000000', // 导航栏背景颜色-初始值
77 | navBackgroundRoll: '#ffffff', // 导航栏背景颜色-滚动值
78 | titleColorInit: '#ffffff', // 文本颜色-初始值 16进制
79 | titleColorRoll: '#000000', // 文本颜色-滚动值 16进制
80 | titleTextInit: '初始标题', // 标题文字-初始值
81 | titleTextRoll: '滚动标题', // 标题文字-滚动值
82 | historyShow: true, // 历史图标是否显示
83 | scrollMin: 50, // 最小滚动间距,单位px
84 | scrollMax: 200, // 最大滚动间距,单位px
85 | homeShow: true, // home图标是否显示
86 | homeJudgeStack: false, // home图标显示是否判断页面栈
87 | homePath: '/pages/index/index', // home页面路径
88 | homeColorInit: 'white', // home图标颜色-初始值 white / black
89 | homeColorRoll: 'black', // home图标颜色-滚动值 white / black
90 | })
91 | this.setData({
92 | navHeight: this.selectComponent('#comp-nav-dynamic').getNavHeight(),
93 | })
94 | },
95 | onPageScroll(e) {
96 | this.selectComponent('#comp-nav-dynamic').scrollHandle(e.scrollTop);
97 | },
98 | })
99 | ```
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | //app.js
2 | App({
3 | onLaunch: function () {
4 | },
5 | globalData: {
6 | userInfo: null
7 | }
8 | })
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | "pages/index/index"
4 | ],
5 | "window": {
6 | "backgroundTextStyle": "light",
7 | "navigationBarBackgroundColor": "#fff",
8 | "navigationBarTitleText": "WeChat",
9 | "navigationBarTextStyle": "black",
10 | "navigationStyle": "custom"
11 | },
12 | "usingComponents": {
13 | "comp-nav-dynamic": "/components/nav-dynamic/nav-dynamic"
14 | },
15 | "sitemapLocation": "sitemap.json"
16 | }
--------------------------------------------------------------------------------
/app.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minigrasshopper/nav-dynamic/b93ef1bc06d77a4d62145fbb4a284e6ceb4877d8/app.wxss
--------------------------------------------------------------------------------
/components/nav-dynamic/icon_home_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minigrasshopper/nav-dynamic/b93ef1bc06d77a4d62145fbb4a284e6ceb4877d8/components/nav-dynamic/icon_home_black.png
--------------------------------------------------------------------------------
/components/nav-dynamic/icon_home_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minigrasshopper/nav-dynamic/b93ef1bc06d77a4d62145fbb4a284e6ceb4877d8/components/nav-dynamic/icon_home_white.png
--------------------------------------------------------------------------------
/components/nav-dynamic/nav-dynamic.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 动态变化的nav组件
3 | */
4 | Component({
5 | options: {
6 | multipleSlots: true
7 | },
8 | properties: {},
9 | data: {
10 | compatible: wx.getSystemInfoSync().SDKVersion > '2.4.3' ? true : true, // 是否兼容,原生最低兼容基础库2.4.3版本,低于限制版本时,隐藏组件
11 | navHeight: 0, // 导航栏高度,单位px
12 | statusHeight: 0, // 状态栏高度,单位px
13 | navOpacity: 0, // 导航栏的透明度,初始=1,到最小滚动点=0,到最大滚动点=1
14 | titleSize: wx.getSystemInfoSync().fontSizeSetting, // 字体大小
15 | navBackground: '', // 导航栏背景颜色
16 | titleColor: '', // 标题颜色
17 | titleText: '', // 标题文本
18 | historyShow: false, // 历史图标是否显示
19 | homeShow: false, // home图标是否显示
20 | homePath: '', // home页面路径
21 | homeColor: '', // home图标颜色
22 | options: {
23 | navBackgroundInit: '#000000', // 导航栏背景颜色-初始值
24 | navBackgroundRoll: '#ffffff', // 导航栏背景颜色-滚动值
25 | titleColorInit: '#ffffff', // 文本颜色-初始值 16进制
26 | titleColorRoll: '#000000', // 文本颜色-滚动值 16进制
27 | titleTextInit: '', // 标题文字-初始值
28 | titleTextRoll: '', // 标题文字-滚动值
29 | historyShow: true, // 历史图标是否显示
30 | scrollMin: 50, // 最小滚动间距,单位px
31 | scrollMax: 200, // 最大滚动间距,单位px
32 | homeShow: false, // home图标是否显示
33 | homeJudgeStack: true, // home图标显示是否判断页面栈
34 | homePath: '/pages/index/index', // home页面路径
35 | homeColorInit: 'white', // home图标颜色-初始值 white / black
36 | homeColorRoll: 'black', // home图标颜色-滚动值 white / black
37 | },
38 | },
39 | attached() {
40 | this.init();
41 | },
42 | methods: {
43 | init() {
44 | // 组件初始化
45 | this.initData();
46 | this.setNavHeight();
47 | },
48 | initData() {
49 | // 更新data数据
50 | this.setData({
51 | navOpacity: 1,
52 | navBackground: this.data.options.navBackgroundInit,
53 | titleColor: this.data.options.titleColorInit,
54 | titleText: this.data.options.titleTextInit,
55 | homePath: this.data.options.homePath,
56 | homeColor: this.data.options.homeColorInit,
57 | })
58 | wx.setNavigationBarColor({
59 | backgroundColor: '',
60 | frontColor: this.data.titleColor,
61 | })
62 | this.updateHistory();
63 | this.updateHome();
64 | },
65 | updateHistory() {
66 | // 更新历史图标的状态
67 | if (!this.data.options.historyShow) {
68 | this.setData({
69 | historyShow: false
70 | })
71 | return
72 | }
73 | // 如果页面栈数量小于2,不显示图标
74 | let routes = getCurrentPages();
75 | if (routes.length < 2) {
76 | this.setData({
77 | historyShow: false
78 | })
79 | } else {
80 | this.setData({
81 | historyShow: true
82 | })
83 | }
84 | },
85 | updateHome() {
86 | // 更新home图标的状态
87 | if (!this.data.options.homeShow) {
88 | this.setData({
89 | homeShow: false
90 | })
91 | return
92 | }
93 | // homeShow && !homeJudgeStack,一直显示
94 | if (this.data.options.homeShow && !this.data.options.homeJudgeStack) {
95 | this.setData({
96 | homeShow: true
97 | })
98 | return
99 | }
100 | // homeShow && homeJudgeStack,如果页面栈数量小于2,显示图标
101 | if (this.data.options.homeShow && this.data.options.homeJudgeStack) {
102 | let routes = getCurrentPages();
103 | if (routes.length < 2) {
104 | this.setData({
105 | homeShow: true
106 | })
107 | } else {
108 | this.setData({
109 | homeShow: false
110 | })
111 | }
112 | return
113 | }
114 | },
115 | setNavHeight() {
116 | // 设置nav高度 = 状态栏高度 + 定值
117 | if (!this.data.compatible) {
118 | this.data.navHeight = 0;
119 | return
120 | }
121 | let statusHeight = wx.getSystemInfoSync().statusBarHeight;
122 | this.setData({
123 | navHeight: statusHeight + 45,
124 | statusHeight: statusHeight,
125 | })
126 | },
127 | getNavHeight() {
128 | // 获取导航栏高度px
129 | return this.data.navHeight;
130 | },
131 | setOptions(options = {}) {
132 | // 设置options参数
133 | let target = JSON.parse(JSON.stringify(this.data.options));
134 | Object.assign(target, options);
135 | this.data.options = target;
136 | this.initData();
137 | },
138 | scrollHandle(scrollTop = 0) {
139 | // 页面滚动事件回调
140 | let navOpacity = '';
141 | let navBackground = '';
142 | let titleColor = '';
143 | let titleText = '';
144 | let homeColor = '';
145 | if (scrollTop <= this.data.options.scrollMin) {
146 | navOpacity = 1;
147 | navBackground = this.data.options.navBackgroundInit;
148 | titleColor = this.data.options.titleColorInit;
149 | titleText = this.data.options.titleTextInit;
150 | homeColor = this.data.options.homeColorInit;
151 | } else if (scrollTop >= this.data.options.scrollMax) {
152 | navOpacity = 1;
153 | navBackground = this.data.options.navBackgroundRoll;
154 | titleColor = this.data.options.titleColorRoll;
155 | titleText = this.data.options.titleTextRoll;
156 | homeColor = this.data.options.homeColorRoll;
157 | } else {
158 | navOpacity = (scrollTop - this.data.options.scrollMin) / (this.data.options.scrollMax - this.data.options.scrollMin);
159 | navBackground = this.data.options.navBackgroundRoll;
160 | titleColor = this.data.options.titleColorRoll;
161 | titleText = this.data.options.titleTextRoll;
162 | homeColor = this.data.options.homeColorRoll;
163 | }
164 | this.setData({
165 | navOpacity,
166 | navBackground,
167 | titleColor,
168 | titleText,
169 | homeColor,
170 | })
171 | wx.setNavigationBarColor({
172 | backgroundColor: '',
173 | frontColor: this.data.titleColor,
174 | })
175 | },
176 | historyHandle() {
177 | // 触发返回按钮
178 | wx.navigateBack();
179 | },
180 | homeHandle(){
181 | // 跳转home路由
182 | let url = this.data.homePath;
183 | wx.switchTab({
184 | url,
185 | fail: () => {
186 | // switchTab失败回调
187 | wx.redirectTo({
188 | url,
189 | fail: () => {
190 | // redirectTo失败回调
191 | wx.reLaunch({
192 | url,
193 | })
194 | }
195 | })
196 | }
197 | })
198 | }
199 | }
200 | })
--------------------------------------------------------------------------------
/components/nav-dynamic/nav-dynamic.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true
3 | }
--------------------------------------------------------------------------------
/components/nav-dynamic/nav-dynamic.less:
--------------------------------------------------------------------------------
1 | view,
2 | cover-view,
3 | cover-image,
4 | text,
5 | swiper,
6 | swiper-item,
7 | input,
8 | picker,
9 | textarea,
10 | form {
11 | box-sizing: border-box;
12 | }
13 |
14 | .flex(@direction: row, @justify: center, @align: center) {
15 | display: flex;
16 | flex-direction: @direction;
17 | justify-content: @justify;
18 | align-items: @align;
19 | }
20 |
21 | .ant-nav-dynamic-wrapper {
22 | position: fixed;
23 | z-index: 99999;
24 | left: 0;
25 | top: 0;
26 | width: 750rpx;
27 | .flex(column, flex-start, flex-start);
28 |
29 | .ant-nav-status-area {
30 | width: 100%;
31 | }
32 |
33 | .ant-nav-content-area {
34 | position: relative;
35 | width: 100%;
36 | flex: 1;
37 |
38 | .ant-nav-history-home-box {
39 | .flex(row, flex-start, center);
40 |
41 | .ant-nav-history-chunk {
42 | width: 80rpx;
43 | height: 100%;
44 | .flex(row, center, center);
45 |
46 | .arrow-back {
47 | transform: rotate(45deg);
48 | border: 4rpx solid transparent;
49 | border-top: none;
50 | border-right: none;
51 | width: 24rpx;
52 | height: 24rpx;
53 | }
54 | }
55 |
56 | .ant-nav-home {
57 | width: 40rpx;
58 | height: 40rpx;
59 |
60 | &.hasgap {
61 | margin-left: 20rpx;
62 | }
63 | }
64 | }
65 |
66 | .ant-nav-title-box {
67 | position: absolute;
68 | left: 50%;
69 | top: 50%;
70 | transform: translateX(-50%) translateY(-50%);
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/components/nav-dynamic/nav-dynamic.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{titleText}}
14 |
15 |
16 |
--------------------------------------------------------------------------------
/components/nav-dynamic/nav-dynamic.wxss:
--------------------------------------------------------------------------------
1 | view,
2 | cover-view,
3 | cover-image,
4 | text,
5 | swiper,
6 | swiper-item,
7 | input,
8 | picker,
9 | textarea,
10 | form {
11 | box-sizing: border-box;
12 | }
13 | .ant-nav-dynamic-wrapper {
14 | position: fixed;
15 | z-index: 99999;
16 | left: 0;
17 | top: 0;
18 | width: 750rpx;
19 | display: flex;
20 | flex-direction: column;
21 | justify-content: flex-start;
22 | align-items: flex-start;
23 | }
24 | .ant-nav-dynamic-wrapper .ant-nav-status-area {
25 | width: 100%;
26 | }
27 | .ant-nav-dynamic-wrapper .ant-nav-content-area {
28 | position: relative;
29 | width: 100%;
30 | flex: 1;
31 | }
32 | .ant-nav-dynamic-wrapper .ant-nav-content-area .ant-nav-history-home-box {
33 | display: flex;
34 | flex-direction: row;
35 | justify-content: flex-start;
36 | align-items: center;
37 | }
38 | .ant-nav-dynamic-wrapper .ant-nav-content-area .ant-nav-history-home-box .ant-nav-history-chunk {
39 | width: 80rpx;
40 | height: 100%;
41 | display: flex;
42 | flex-direction: row;
43 | justify-content: center;
44 | align-items: center;
45 | }
46 | .ant-nav-dynamic-wrapper .ant-nav-content-area .ant-nav-history-home-box .ant-nav-history-chunk .arrow-back {
47 | transform: rotate(45deg);
48 | border: 4rpx solid transparent;
49 | border-top: none;
50 | border-right: none;
51 | width: 24rpx;
52 | height: 24rpx;
53 | }
54 | .ant-nav-dynamic-wrapper .ant-nav-content-area .ant-nav-history-home-box .ant-nav-home {
55 | width: 40rpx;
56 | height: 40rpx;
57 | }
58 | .ant-nav-dynamic-wrapper .ant-nav-content-area .ant-nav-history-home-box .ant-nav-home.hasgap {
59 | margin-left: 20rpx;
60 | }
61 | .ant-nav-dynamic-wrapper .ant-nav-content-area .ant-nav-title-box {
62 | position: absolute;
63 | left: 50%;
64 | top: 50%;
65 | transform: translateX(-50%) translateY(-50%);
66 | }
67 |
--------------------------------------------------------------------------------
/pages/index/index.js:
--------------------------------------------------------------------------------
1 | Page({
2 | data: {
3 | navHeight: 0,
4 | },
5 | onLoad() {
6 | this.setNav();
7 | },
8 | setNav() {
9 | this.selectComponent('#comp-nav-dynamic').setOptions({
10 | navBackgroundInit: '#000000', // 导航栏背景颜色-初始值
11 | navBackgroundRoll: '#ffffff', // 导航栏背景颜色-滚动值
12 | titleColorInit: '#ffffff', // 文本颜色-初始值 16进制
13 | titleColorRoll: '#000000', // 文本颜色-滚动值 16进制
14 | titleTextInit: '初始标题', // 标题文字-初始值
15 | titleTextRoll: '滚动标题', // 标题文字-滚动值
16 | historyShow: true, // 历史图标是否显示
17 | scrollMin: 50, // 最小滚动间距,单位px
18 | scrollMax: 200, // 最大滚动间距,单位px
19 | homeShow: true, // home图标是否显示
20 | homeJudgeStack: false, // home图标显示是否判断页面栈
21 | homePath: '/pages/index/index', // home页面路径
22 | homeColorInit: 'white', // home图标颜色-初始值 white / black
23 | homeColorRoll: 'black', // home图标颜色-滚动值 white / black
24 | })
25 | this.setData({
26 | navHeight: this.selectComponent('#comp-nav-dynamic').getNavHeight(),
27 | })
28 | },
29 | onPageScroll(e) {
30 | this.selectComponent('#comp-nav-dynamic').scrollHandle(e.scrollTop);
31 | },
32 | })
--------------------------------------------------------------------------------
/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
--------------------------------------------------------------------------------
/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 | 我是插槽
3 |
4 |
5 |
--------------------------------------------------------------------------------
/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | .container{
2 | width: 750rpx;
3 | height: 2000rpx;
4 | background: url('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1562668064379&di=f57f94d258edb4eb17e6cd568c6431c0&imgtype=0&src=http%3A%2F%2Fuploads.5068.com%2Fallimg%2F1801%2F81-1P10Q60023.jpg') no-repeat;
5 | background-size: cover;
6 | }
--------------------------------------------------------------------------------
/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件",
3 | "packOptions": {
4 | "ignore": []
5 | },
6 | "setting": {
7 | "urlCheck": true,
8 | "es6": true,
9 | "postcss": true,
10 | "minified": true,
11 | "newFeature": true,
12 | "autoAudits": false
13 | },
14 | "compileType": "miniprogram",
15 | "libVersion": "2.7.3",
16 | "appid": "wx2e31e3522a35edb7",
17 | "projectname": "nav-dynamic",
18 | "debugOptions": {
19 | "hidedInDevtools": []
20 | },
21 | "isGameTourist": false,
22 | "simulatorType": "wechat",
23 | "simulatorPluginLibVersion": {},
24 | "condition": {
25 | "search": {
26 | "current": -1,
27 | "list": []
28 | },
29 | "conversation": {
30 | "current": -1,
31 | "list": []
32 | },
33 | "game": {
34 | "currentL": -1,
35 | "list": []
36 | },
37 | "miniprogram": {
38 | "current": -1,
39 | "list": []
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/sitemap.json:
--------------------------------------------------------------------------------
1 | {
2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
3 | "rules": [{
4 | "action": "allow",
5 | "page": "*"
6 | }]
7 | }
--------------------------------------------------------------------------------