微信小程序-自定义菜单导航(实现楼梯效果)
设计初衷
在开发页面时,往往需要实现,点击页面的导航菜单页面滚动到相应位置,滚动页面实现菜单选项的高亮。在html开发中,我们可以用到a标签锚点实现,jq的动画相结合实现类似效果。在框架中vant UI框架也为我们实现了这一效果。
微信小程序该如何实现??
效果展示
- 当菜单导航滚动到页面顶部时,菜单吸顶
- 当点击菜单按钮时,切换到对应区域(过渡到该区域,有动画效果)
- 当内容区滚动到某类区域时,对应区域的菜单按钮高亮

设计思路
1、吸顶效果的实现
- 获取菜单导航距离页面顶部距离
wx.createSelectorQuery() - 页面滚动监听
- 滚动距离与菜单初始位置值比较
1) 距离
const query = wx.createSelectorQuery()
query.select('.menu_nav').boundingClientRect(function(res) {
let obj = {}
if (res && res.top) {
obj[item.attr] = parseInt(res.top)
}
}).exec()
①wx.createSelectorQuery()
返回一个 SelectorQuery 对象实例。在自定义组件或包含自定义组件的页面中,应使用 this.createSelectorQuery() 来代替。
②SelectorQuery.select(string selector)
在当前页面下选择第一个匹配选择器 selector 的节点。返回一个 NodesRef 对象实例,可以用于获取节点信息。
selector 语法
selector类似于 CSS 的选择器,但仅支持下列语法。
- ID选择器:#the-id
- class选择器(可以连续指定多个):.a-class.another-class
- 子元素选择器:.the-parent > .the-child
- 后代选择器:.the-ancestor .the-descendant
- 跨自定义组件的后代选择器:.the-ancestor >>> .the-descendant
- 多选择器的并集:#a-node, .some-other-nodes
③NodesRef.boundingClientRect(function callback)
添加节点的布局位置的查询请求。相对于显示区域,以像素为单位。其功能类似于 DOM 的 getBoundingClientRect。返回 NodesRef 对应的 SelectorQuery。
| 属性 | 类型 | 说明 |
|---|---|---|
| id | string | 节点的 ID |
| dataset | Object | 节点的 dataset |
| left | number | 节点的左边界坐标 |
| right | number | 节点的右边界坐标 |
| top | number | 节点的上边界坐标 |
| bottom | number | 节点的下边界坐标 |
| width | number | 节点的宽度 |
| height | number | 节点的高度 |
④SelectorQuery.exec(function callback)
执行所有的请求。请求结果按请求次序构成数组,在callback的第一个参数中返回。
2) 页面滚动监听
- data中初始化--
tabFixed=false(表示是否固定定位) - 滚动条滚动距离超过了菜单初始距离时,
tabFixed=true开启定位
// 监听页面滚动
onPageScroll: function(e) {
let hTop = parseInt(e.scrollTop)
// 菜单是否需要定位到顶部
if (hTop > this.data.menu_top) {
this.setData({
tabFixed: true
})
} else {
this.setData({
tabFixed: false
})
}
}
onPageScroll(Object object))
监听用户滑动页面事件。
参数 Object object:
| 属性 | 类型 | 说明 |
|---|---|---|
| scrollTop | Number | 页面在垂直方向已滚动的距离(单位px) |
注意:请只在需要的时候才在 page 中定义此方法,不要定义空方法。以减少不必要的事件派发对渲染层-逻辑层通信的影响。 注意:请避免在 onPageScroll 中过于频繁的执行 setData 等引起逻辑层-渲染层通信的操作。尤其是每次传输大量数据,会影响通信耗时。
2、切换到对应区域
- 记录当前点击的菜单并高亮
- 获取每个区域初始距离页面顶部距离
- 设置当前页面滚动条滚动到的位置,设置过度时间
// 导航栏切换设置
setSelectType(event) {
let index = event.currentTarget.dataset.type
this.setData({
tabIndex: index,
})
let arr = ['panel1_top', 'panel2_top', 'panel3_top', 'panel4_top']
let _this = this
wx.pageScrollTo({
scrollTop: _this.data[arr[index]],
duration: 500
})
},
wx.pageScrollTo(Object object)
将页面滚动到目标位置,支持选择器和滚动距离两种方式定位
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| scrollTop | number | 无 | 否 | 滚动到页面的目标位置,单位 px |
| duration | number | 300 | 否 | 滚动动画的时长,单位 ms |
| selector | string | 无 | 否 | 选择器 2.7.3 |
| success | function | 无 | 否 | 接口调用成功的回调函数 |
| fail | function | 无 | 否 | 接口调用失败的回调函数 |
| complete | unction | 无 | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) |
3) 滚动到某类区域时,对应区域的菜单按钮高亮
- 获取初始时区域距离顶端距离
let arr = [
{ name: '.menu-nav', attr: 'menu_top', addNum: 0 },
{ name: '.panel1', attr: 'panel1_top', addNum: 0 },
{ name: '.panel2', attr: 'panel2_top', addNum: 0 },
{ name: '.panel3', attr: 'panel3_top', addNum: 0 },
{ name: '.panel4', attr: 'panel4_top', addNum: 0 },
]
arr.forEach((item, i) => {
wx.createSelectorQuery().select(item.name).boundingClientRect(function(res) {
let obj = {}
if (res && res.top) {
obj[item.attr] = parseInt(res.top) if (item.addNum) {
obj[item.attr] += item.addNum
}
that.setData({
...obj
})
} }).exec() })
- 滚动监听是否超过了该区域
// 监听页面滚动
onPageScroll: function(e) {
let hTop = parseInt(e.scrollTop)
// 自动切换菜单
let tab=0
if (hTop >= (this.data['panel4_top'] - this.data.menu_top)) {
tab=3
}else if (hTop >= (this.data['panel3_top'] - this.data.menu_top)){
tab=2
}
else if (hTop >= (this.data['panel2_top'] - this.data.menu_top)){
tab=1
}
this.setData({
tabIndex: tab,
})
},
完整代码
index.js
// pages/index/index.js
Page({
/**
* 页面的初始数据
*/
data: {
tabIndex: 0, //当前处于那个菜单
menuList: ['菜单1', '菜单2', '菜单3', '菜单4'], //导航菜单
tabFixed: false, //是否定位
// 初始页面距离顶部距离
menu_top: 0,
panel1_top: 0,
panel2_top: 0,
panel3_top: 0,
panel4_top: 0,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
onShow:function (options){
this.getTopDistance()
},
// 获取距离页面顶部高度
getTopDistance() {
let that = this
let arr = [{
name: '.menu-nav',
attr: 'menu_top',
addNum: 0
},
{
name: '.panel1',
attr: 'panel1_top',
addNum: 0
},
{
name: '.panel2',
attr: 'panel2_top',
addNum: 0
},
{
name: '.panel3',
attr: 'panel3_top',
addNum: 0
},
{
name: '.panel4',
attr: 'panel4_top',
addNum: 0
},
]
arr.forEach((item, i) => {
wx.createSelectorQuery().select(item.name).boundingClientRect(function (res) {
let obj = {}
if (res && res.top) {
obj[item.attr] = parseInt(res.top)
if (item.addNum) {
obj[item.attr] += item.addNum
}
that.setData({
...obj
})
}
}).exec()
})
},
// 导航栏切换设置
setSelectType(event) {
let index = event.currentTarget.dataset.type
this.setData({
tabIndex: index,
})
let arr = ['panel1_top', 'panel2_top', 'panel3_top', 'panel4_top']
let _this = this
wx.pageScrollTo({
scrollTop: _this.data[arr[index]],
duration: 500
})
},
// 监听页面滚动
onPageScroll: function (e) {
let hTop = parseInt(e.scrollTop)
// 菜单是否需要定位到顶部
if (hTop > this.data.menu_top) {
this.setData({
tabFixed: true
})
} else {
this.setData({
tabFixed: false
})
}
// 自动切换菜单
if (hTop >= (this.data['panel4_top'] - this.data.menu_top)) {
this.setData({
tabIndex: 3,
})
}else if (hTop >= (this.data['panel3_top'] - this.data.menu_top)){
this.setData({
tabIndex: 2,
})
}
else if (hTop >= (this.data['panel2_top'] - this.data.menu_top)){
this.setData({
tabIndex: 1,
})
}else{
this.setData({
tabIndex: 0,
})
}
},
})
index.wxml
<view class="Main">
<view class="head">
我是头部区域
</view>
<view class="{{tabFixed?'is-fixed':''}} menu-nav">
<text wx:for="{{menuList}}" class="{{tabIndex==index?'is-select':''}}" bind:tap="setSelectType" data-type='{{index}}'>{{item}}</text>
</view>
<view class="content">
<view class="panel1 panel">页面1</view>
<view class="panel2 panel">页面2</view>
<view class="panel3 panel">页面3</view>
<view class="panel4 panel">页面4</view>
</view>
</view>
index.wxss
.menu-nav {
display: flex;
align-items: center;
justify-content: space-around;
color: black;
padding: 10px 0;
width: 100%;
background-color: white;
}
.is-select {
color: red;
}
.head {
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
height: 120px;
background-color: greenyellow;
}
.is-fixed {
position: fixed;
top: 0;
}
.panel {
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
}
.panel1 {
height: 800rpx;
background-color: rebeccapurple;
}
.panel2 {
height: 700rpx;
background-color: blue;
}
.panel3 {
height: 1000rpx;
background-color: orange;
}
.panel4 {
height: 1200rpx;
background-color: pink;
}
微信小程序-自定义菜单导航(实现楼梯效果)的更多相关文章
- 微信小程序自定义下导航页面切换效果的合理写法
上图::: 导航模板内容页面的定义: <template name="naviBot"> <view class='navwrap t_cen font_26 ...
- 微信小程序-自定义底部导航
代码地址如下:http://www.demodashi.com/demo/14258.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...
- 微信小程序自定义底部导航栏组件+跳转
微信小程序本来封装有底部导航栏,但对于想自定义样式和方法的开发者来说,这并不是很好. 参考链接:https://github.com/ljybill/miniprogram-utils/tree/ma ...
- 微信小程序自定义顶部导航
注释:自定义导航需要自备相应图片 一.设置自定义顶部导航 Navigation是小程序的顶部导航组件,当页面配置navigationStyle设置为custom的时候可以使用此组件替代原生导航栏. 1 ...
- 微信小程序 自定义头部导航栏和导航栏背景图片 navigationStyle
这两天因为要做一个带背景的小程序头,哭了,小程序导航栏有背景也就算了,还得让导航栏上的背景顺下来,心态小崩.现在可以单独设置一个页面的小程序头了,但是前提是要微信7.0以上的版本,考虑到兼容性问题 ...
- 微信小程序自定义头部导航栏
<!--index.wxml--> <view> <navbar id='index_header' background='{{background}}' pageNa ...
- 微信小程序——自定义菜单切换栏tabbar组件
效果图: wxml代码: <view class="top_tabbar" > <block wx:for="{{itemName}}" wx ...
- 微信小程序——自定义导航栏
微信头部导航栏可能通过json配置: 但是有时候我们项目需求可能需要自定义头部导航栏,如下图所示: 现在具体说一下实现步骤及方法: 步骤: 1.在 app.json 里面把 "navigat ...
- 微信小程序 自定义导航组件 nav头部 全面屏设计
nav-dynamic 微信小程序自定义nav头部组件:适配全面屏设计: 实现功能 初始进入页面时,展示初始状态下的nav样式: 页面滚动时,监听页面滚动事件,展示滚动状态下的nav样式: 根据配置字 ...
随机推荐
- IT四大名著
标题耸人听闻,sorry. CPU.操作系统.编译器和数据库我都不会.我英语也不行,但我认识所有的字母.:-) 万一有人感兴趣呢?https://sqlite.org/doclist.htmlThe ...
- A Child's History of England.39
He had become Chancellor, when the King thought of making him Archbishop. He was clever, gay, well e ...
- 大数据学习day25------spark08-----1. 读取数据库的形式创建DataFrame 2. Parquet格式的数据源 3. Orc格式的数据源 4.spark_sql整合hive 5.在IDEA中编写spark程序(用来操作hive) 6. SQL风格和DSL风格以及RDD的形式计算连续登陆三天的用户
1. 读取数据库的形式创建DataFrame DataFrameFromJDBC object DataFrameFromJDBC { def main(args: Array[String]): U ...
- Linux基础命令---lynx浏览器
lynx lynx是一个字符界面的全功能www浏览器,它没有图形界面,因此占用的资源较少. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.Fedora. 1.语法 ...
- Linux基础命令---slabtop
slabtop slabtop实时显示详细的内核板条缓存信息.它显示按所列排序条件之一排序的顶级缓存的列表.它还会显示一个统计信息头,其中填充了板坯层信息. 此命令的适用范围:RedHat.RHEL. ...
- InnoDB学习(三)之BinLog
BinLog又称为二进制日志,是MySQL服务层的数据日志,MySQL所有的存储引擎都支持BinLog.BinLog记录了MySQL中的数据更新和可能导致数据更新的事件,可以用于主从复制或数据恢复.本 ...
- 淘宝网购物车jquery源码和网易新用户注册页面表单验证的练习
淘宝网购物车源码: <html lang="en"> <head> <meta charset="UTF-8"> <t ...
- 数据恢复binlog2sql
目录 一.原理及其使用 用途 闪回原理简析 binlog 有三种可选的格式: 来实例演习下来实例演习下 二.准备工作 一.原理及其使用 生产上误删数据.误改数据的现象也是时常发生的现象,作为运维这时候 ...
- Flutter 2.8 更新详解
北半球的冬意已至,黄叶与气温均随风而落.年终的最后一个 Flutter 稳定版本 已悄然来到你的面前.让我们向 Flutter 2.8 打声招呼- 本次更新包含了 207 位贡献者和 178 位审核者 ...
- 再识ret2syscall
当初学rop学到的ret2syscall,对int 0x80中断了解还不是很深,这次又复习了一遍.虽然很简单,但是还是学到了新东西.那么我们就从ret2syscall开始吧. IDA一打开的时候,就看 ...