├── 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 | } --------------------------------------------------------------------------------