左侧分类菜单按钮,并且右侧还有同步的吸顶式菜单标题,这种效果我常在现在的点餐、菜谱类程序中见到,此篇文章介绍我在微信小程序中实现这一效果的一种方案,先看示例效果视频:
右侧吸顶的菜单标题使用的是 position: sticky;
实现方案,这里要注意微信小程序中 scroll-view 组件一定要按要求设置 scroll-y 等属性,而且一定要设置其高度为非百分比的单位,否则其滚动事件不能正常响应,而且 sticky 定位的元素也不能正常生效。
wxml 部位代码:
<view id="view_page_box">
<scroll-view scroll-y="{{true}}" id="scroll_view_menu_box">
<view class='VIEW_menu {{index==current_content_box_index?"selected":""}}' wx:for="{{5}}" data-index="{{index}}" bind:tap="selectMenu">
Header {{item+1}}
</view>
</scroll-view>
<scroll-view scroll-y="{{true}}" scroll-top="{{scroll_view_top}}" scroll-with-animation="{{true}}" id="scroll_view_content_box" bindscroll="onViewBoxScroll">
<view class="VIEW_content_box" wx:for="{{5}}" wx:for-index="idx_header">
<view class="VIEW_header">Header {{idx_header+1}}</view>
<view class="VIEW_item" wx:for="{{8}}" wx:for-index="idx_item">item {{idx_header+1}}_{{idx_item+1}}</view>
</view>
</scroll-view>
</view>
不知为什么,scroll-view 组件的 scroll-top 属性并不能进行双向绑定。
wxss 部位代码:
#view_page_box {
display: flex;
}
#scroll_view_menu_box {
width: 120px;
height: 480px;
}
.VIEW_menu {
display: block;
padding: 10px;
}
.VIEW_menu.selected {
background: gainsboro;
}
#scroll_view_content_box {
height: 480px;
background: oldlace;
}
.VIEW_header {
display: block;
padding: 10px;
font-weight: bold;
font-size: 1.5em;
background: bisque;
position: sticky;
top: 0;
}
.VIEW_item {
display: block;
padding: 10px;
}
sticky 定位的元素需要再加上 top、right、bottom、left 属性一起使用,否则不会生效。
js 部位代码:
Page({
data: {
content_box_offset_tops: [],
current_content_box_index: 0,
scroll_view_top: 0
},
onLoad(options) {
var that = this
var query = that.createSelectorQuery()
query.selectAll(".VIEW_content_box").fields({
rect: true
}, function (res) {
var content_box_offset_tops = new Array()
res.forEach(function (item) {
content_box_offset_tops.push(item.top)
})
that.setData({
content_box_offset_tops
})
}).exec()
},
onViewBoxScroll(e) {
var that = this
var scrollTop = e.detail.scrollTop
var min_d_value = Number.MAX_SAFE_INTEGER
var current_content_box_index = 0
that.data.content_box_offset_tops.forEach(function (item, index) {
var d_value = Math.abs(scrollTop - item)
if (d_value < min_d_value) {
min_d_value = d_value
current_content_box_index = index
}
})
that.setData({
current_content_box_index
})
},
selectMenu(e) {
var that = this
var current_content_box_index = e.currentTarget.dataset.index
that.setData({
current_content_box_index,
scroll_view_top: that.data.content_box_offset_tops[current_content_box_index]
})
}
})
在页面载入后,计算出右侧内容盒子的位置偏移量,这些数据用来配合后续的点击左侧按钮进行滚动定位的功能与随滚动同步更新左侧选中按钮状态的功能;在右侧 scroll-view 滚动时,计算出当前滚动的位置与哪一个内容盒子最相近,用来变化左侧按钮的选中状态;点击左侧按钮的功能就直接用载入时计算出的位置偏移量进行右侧 scroll-view 的滚动就行了。
结语:此示例的最终效果并不完全理想,在右侧 scroll-view 滚动的一些情况下,能够见到吸顶的标题在滚动动画后闪现出来,我想这可能是使用 sticky 定位的本来效果,如果用脚本计算代码手动实现右侧的吸顶标题也许效果会更好;我个人其实不怎么喜欢这种布局效果,因为考虑到在实际的生产环境中如果让一页的数据量变大的话会影响程序整体效果的流畅度,而且集成度高又需要实时同步的界面改动、维护起来会变得更麻烦。