设计初衷

在开发页面时,往往需要实现,点击页面的导航菜单页面滚动到相应位置,滚动页面实现菜单选项的高亮。在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. 浅讲.Net 6 之 WebApplicationBuilder

    介绍 .Net 6为我们带来的一种全新的引导程序启动的方式.与之前的拆分成Program.cs和Startup不同,整个引导启动代码都在Program.cs中. WebApplicationBuild ...

  2. jmeter设置参数化

    设置参数化方法有3种 第一种: 1.打开 jmeter,导入badboy录制的脚本 导入后记得选择"step"右键选择change controller ->逻辑控制器-&g ...

  3. SpringBoot的定时任务

    springBoot定时任务可分为多线程和单线程,而单线程又分为注解形式,接口形式 1.基于注解形式 基于注解@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影 ...

  4. jQuery遍历的几种方式

    一.jQuery对象遍历 1 <script type="text/javascript" src="js/jquery-3.4.1.js">< ...

  5. spring的核心容器ApplicationContext

    //bean.xml配置文件 <?xml version="1.0" encoding="UTF-8"?><beans xmlns=" ...

  6. 【MySQL】查询不在表中的数据

    1.方法一(仅适用单个字段):使用 not in ,比较容易理解,缺点是效率低 如:select A.ID from A where A.ID not in (select ID from B): 2 ...

  7. Tableau如何使用 度量值和度量名称

    一.把子类别拖拽至列 二.度量值拖拽至行 三.度量名称拖拽至筛选器,右键-编辑筛选器-选择销售额和利润 四.度量名称拖拽是标记选择颜色-其它细节调整-最终结果如下所示

  8. proxy跨域

    跨域 浏览器访问非同源的网址时,会被限制访问,出现跨域问题. 解决方案: 1.response 添加 header(CORS) 2.JSONP 方式 3.全局对象+iframe (1)document ...

  9. Android: Client-Server communication by JSON

    Refer to: http://osamashabrez.com/client-server-communication-android-json/ This is a sequel to my l ...

  10. 拆分行(Power Query 之 M 语言)

    数据源: 略 目标: 将指定列拆分为行 操作过程: 选取指定列>[主页](或[转换])>[拆分列]>[按分隔符]/[按字符数]/[按位置]>高级选项>拆分为[行] M公式 ...