设计初衷

在开发页面时,往往需要实现,点击页面的导航菜单页面滚动到相应位置,滚动页面实现菜单选项的高亮。在html开发中,我们可以用到a标签锚点实现,jq的动画相结合实现类似效果。在框架中vant UI框架也为我们实现了这一效果。

微信小程序该如何实现??

效果展示

  1. 当菜单导航滚动到页面顶部时,菜单吸顶
  2. 当点击菜单按钮时,切换到对应区域(过渡到该区域,有动画效果)
  3. 当内容区滚动到某类区域时,对应区域的菜单按钮高亮

设计思路

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、切换到对应区域

  1. 记录当前点击的菜单并高亮
  2. 获取每个区域初始距离页面顶部距离
  3. 设置当前页面滚动条滚动到的位置,设置过度时间
// 导航栏切换设置
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) 滚动到某类区域时,对应区域的菜单按钮高亮
  1. 获取初始时区域距离顶端距离
     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() })
  2. 滚动监听是否超过了该区域
     // 监听页面滚动
    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;
}

微信小程序-自定义菜单导航(实现楼梯效果)的更多相关文章

  1. 微信小程序自定义下导航页面切换效果的合理写法

    上图::: 导航模板内容页面的定义: <template name="naviBot">   <view class='navwrap t_cen font_26 ...

  2. 微信小程序-自定义底部导航

    代码地址如下:http://www.demodashi.com/demo/14258.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...

  3. 微信小程序自定义底部导航栏组件+跳转

    微信小程序本来封装有底部导航栏,但对于想自定义样式和方法的开发者来说,这并不是很好. 参考链接:https://github.com/ljybill/miniprogram-utils/tree/ma ...

  4. 微信小程序自定义顶部导航

    注释:自定义导航需要自备相应图片 一.设置自定义顶部导航 Navigation是小程序的顶部导航组件,当页面配置navigationStyle设置为custom的时候可以使用此组件替代原生导航栏. 1 ...

  5. 微信小程序 自定义头部导航栏和导航栏背景图片 navigationStyle

    ​ 这两天因为要做一个带背景的小程序头,哭了,小程序导航栏有背景也就算了,还得让导航栏上的背景顺下来,心态小崩.现在可以单独设置一个页面的小程序头了,但是前提是要微信7.0以上的版本,考虑到兼容性问题 ...

  6. 微信小程序自定义头部导航栏

    <!--index.wxml--> <view> <navbar id='index_header' background='{{background}}' pageNa ...

  7. 微信小程序——自定义菜单切换栏tabbar组件

    效果图: wxml代码: <view class="top_tabbar" > <block wx:for="{{itemName}}" wx ...

  8. 微信小程序——自定义导航栏

    微信头部导航栏可能通过json配置: 但是有时候我们项目需求可能需要自定义头部导航栏,如下图所示: 现在具体说一下实现步骤及方法: 步骤: 1.在 app.json 里面把 "navigat ...

  9. 微信小程序 自定义导航组件 nav头部 全面屏设计

    nav-dynamic 微信小程序自定义nav头部组件:适配全面屏设计: 实现功能 初始进入页面时,展示初始状态下的nav样式: 页面滚动时,监听页面滚动事件,展示滚动状态下的nav样式: 根据配置字 ...

随机推荐

  1. nodeJs-querystring 模块

    JavaScript 标准参考教程(alpha) 草稿二:Node.js querystring 模块 GitHub TOP querystring 模块 来自<JavaScript 标准参考教 ...

  2. mysql删除数据后不释放空间问题

    如果表的引擎是InnoDB,Delete From 结果后是不会腾出被删除的记录(存储)空间的. 需要执行:optimize table 表名; eg:optimize table eh_user_b ...

  3. javaAPI1

    Iterable<T>接口, Iterator<T> iterator() Collection<E>:接口,add(E e) ,size() , Object[] ...

  4. echo -e "\033[字背景颜色;字体颜色m字符串\033[0m

    格式: echo -e "\033[字背景颜色;字体颜色m字符串\033[0m" 例如: echo -e "\033[41;36m something here \033 ...

  5. idea2019.2安裝MybatisCodeHelper插件

    1. 下载MybatisCodeHelper插件 下载已破解的插件压缩包,一定注意校验sha1sum!!! 在IDEA中本地安装插件 激活方法(自2.7.3):IDEA顶部菜单:Tools -> ...

  6. oracle keep

    语法: min | max(column1) keep (dense_rank first | last order by column2) over (partion by column3); -- ...

  7. 【Linux】【RedHat】下载 安装 注册

    RedHat 下载 安装 注册 记录 因为找入口太麻烦了,所以写了篇博文记录下来大致入口@萌狼蓝天 注册 点击进入注册地址(https://www.redhat.com/wapps/ugc/regis ...

  8. Go语言核心36讲(Go语言实战与应用二十五)--学习笔记

    47 | 基于HTTP协议的网络服务 我们在上一篇文章中简单地讨论了网络编程和 socket,并由此提及了 Go 语言标准库中的syscall代码包和net代码包. 我还重点讲述了net.Dial函数 ...

  9. MySQL记录锁、间隙锁、临键锁小案例演示

    生成间隙(gap)锁.临键(next-key)锁的前提条件 是在 RR 隔离级别下. 有关Mysql记录锁.间隙(gap)锁.临键锁(next-key)锁的一些理论知识之前有写过,详细内容可以看这篇文 ...

  10. 【antd】表单-单页面多表单提交功能

    需求:多个表单,我想一起校验一起提交,比如这里的上下两个部分为两个组件,两个form表单 功能点:1.所有报表需通过校验 2.通过校验后提交并带入所有参数 核心问题:form表单的validateFi ...