设计初衷

在开发页面时,往往需要实现,点击页面的导航菜单页面滚动到相应位置,滚动页面实现菜单选项的高亮。在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. postman测试接口之POST提交本地文件数据
  2. 详解Java GC的工作原理+Minor GC、FullGC
  3. MVC中实现只有当用户登录成功的时候才等浏览内容,否则跳转到登录页面
  4. [NOIP2015] 子串substring 题解
  5. 关于BEA-000402和BEA-000438
  6. Codeforces Round #222 (Div. 1) C. Captains Mode 对弈+dp
  7. Java [Leetcode 144]Binary Tree Preorder Traversal
  8. MyBatis的CRUD操作
  9. Spring中@Autowired注解与自动装配
  10. 初识Selenium(四)
  11. Java(14)继承
  12. java SE 基础概念梳理(一)
  13. vue实现二级联动效果
  14. content-type的几种取值
  15. SQLite3的MFC使用
  16. c博客作业01--顺序、分支结构
  17. linux 服务管理
  18. mysql常用操作小节
  19. python之文件处理
  20. php获取父目录的方法 dirname()

热门文章

  1. Cocoapods 版本更新与更新到指定版本
  2. DOM解析xml学习笔记
  3. 第7章 使用性能利器——Redis
  4. new Date()与setDate()参数
  5. spring整合mybatis和springmvc的配置概图
  6. 深入.NET框架与面向对象的回顾
  7. spring切面-单线程简单权限判定
  8. 【dva】如何监听异步请求是否完成(页面loading)
  9. C# ASP.NET WebApi 跨域设置
  10. ANTLR 语法设计