1.静态效果图如下:
2.代码如下:
(1)在menu.wxml中:
<!--pages/menu/menu.wxml--> <!-- 轮播图 使用组件 --> <banner imgHeight="{{imgHeight}}" backgroundArr="{{backgroundArr}}" /> <view class="main"> <!-- 左侧 --> <scroll-view scroll-y="true" class="left" scroll-into-view="left{{leftId}}"> <view id="left{{item.id}}" bindtap="changeFn" data-id="{{index}}" class="scroll-view-item {{current===index?'active':''}}"
wx:for="{{menuArr}}" wx:key="*this">{{item.title}}
</view> </scroll-view> <!-- 右侧 --> <!-- 要点击左侧时,右侧实现滑动到指定位置,要加scroll-with-animation="true 才能实现过度滚动动画效果--> <scroll-view scroll-y="true" bindscroll="bindscrollFn" class="right" scroll-into-view="right{{rightId}}" scroll-with-animation="true"
showScrollbar="{{showScrollbar}}" enhanced="{{enhanced}}" > <!-- scroll-into-view="right{{rightId}}" 要与 下面这个view的id一致 --> <view id="right{{item.id}}" class="scroll-view-item box" wx:for="{{menuArr}}" wx:key="*this"> <!-- 标题 --> <view class="title">{{item.title}}</view> <view class="content"> <view wx:for="{{item.subArr}}" wx:key="*this" wx:for-item="subItem"> <image src="{{subItem.imgSrc}}" mode="widthFix"></image> <text>{{subItem.imgDesc}}</text> </view> </view> </view> </scroll-view> </view>
(2)在menu.wxss中:
/* pages/menu/menu.wxss */ .main{ width: 100%; height: calc(100vh - 361.11rpx); /*使用rpx单位 避免其他其他机型底部内容被覆盖问题*/ background-color: #efefef; display: flex; justify-content: space-between; overflow: hidden; } .main .left{ width: 25%; height: 100%; background-color: #fff; border-right: 1px solid #ccc; box-sizing: border-box; } .left .scroll-view-item{ width: 100%; height: 100rpx; line-height: 100rpx; text-align: center; border-bottom: 2rpx solid #ccc; font-size: 28rpx; position: relative; } .left .scroll-view-item.active{ background-color: #efefef; } .left .scroll-view-item.active::before{ display: block; } .left .scroll-view-item::before{ content: ""; position: absolute; top: 0; left: 0; width: 8rpx; height: 100%; background-color: #333399; display: none; } .right{ width: 75%; height: 100%; padding:0 4%; box-sizing: border-box; } .right .title{ width: 100%; height: 80rpx; line-height:80rpx; font-weight: bold; } .right .content{ margin-bottom: 20rpx; padding: 20rpx 14rpx 0 0; border-radius: 10rpx; /* 100%; */ background-color: #fff; display: flex; justify-content: flex-start; flex-wrap: wrap; box-sizing: border-box; } .right .content view{ margin-bottom: 10rpx; width: 30%; margin-left: 3.2%; } .right .content view image{ width: 100%; display: block; border-radius: 10rpx; } .right .content view text{ display: block; height: 50rpx; line-height: 50rpx; text-align: center; font-size: 28rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
(3)在menu.js中:
// pages/menu/menu.js Page({ data: { showScrollbar: false, enhanced: true, current: 0, leftId: 0, rightId: 0, rightArr: [], menuArr:[], // 控制menu页面的轮播图高度 为520 imgHeight: 520, // 轮播图数据 backgroundArr: [ "/images/banner/menubanner1.jpg", "/images/banner/menubanner2.jpg", "/images/banner/menubanner3.jpg" ], }, //左侧点击事件 获取每一项上的自定义属性值,给到current 每次点击 对active类名进行添加加或删除 changeFn(e) { // console.log(e.currentTarget.dataset.id); let index = e.currentTarget.dataset.id; this.setData({ current: index, //修改data中的值 leftId: index, rightId: index }) }, // 右侧滚动事件 bindscrollFn(e) { let st = e.detail.scrollTop; //获取右侧内容滚动的高度 let tempArr = this.data.rightArr; //获取data中的rightArr数组 // console.log(tempArr); for(let i=0;i<tempArr.length;i++){ // 每个box都有高度,st在第index个box和(index+1)之间的话,当前就是index // 减去10像素,是因为当点击左侧第二项的时候,第二项头部还有第一项底部的空间,此时会闪跳选中第一项,而第二项没被选中 if(st>=tempArr[i]-10 && st<tempArr[i+1]-10){ // 将数组的索引给到左侧滚动的id this.setData({ current: i, leftId: i, }) return; //当当前项符合条件的时候,结束本次循环,避免符合条件时还在一直循环,导致页面卡顿 } } }, // 相当于 vue 生命周期 mounted getBoxH(){ let _this = this; //存储this,避免this指向被修改报错 // 使用定时器的原因:因为数组在遍历的时候是有延迟的,onReady挂载完成了,但是数组还在渲染, // query.exec抢在页面数组渲染完成前,拿到盒子的高度导致数据不准确, // 使用定时器就是为了等到页面数组的数据渲染完成在获取盒子的高度,这时拿到的数据是比较准确的 setTimeout(() => { const query = wx.createSelectorQuery() query.selectAll('.box').boundingClientRect() //获取所有box盒子的高度 .box是类名 query.selectViewport().scrollOffset() let heightArr = [0];//定义一个第0项值为0的数组,用于存储所有盒子的高度 let baseNum = 0; query.exec(function (res) { // console.log(res[0]); // 遍历res[0],获取其中每一项的height值 res[0].map(val => { //因为比较的高度是当前项高度与当前项高和下一项值高度的和,所以要累加之后再存储到heightArr baseNum +=val.height; heightArr.push(baseNum) }) // console.log(heightArr); // 将heightArr数组数据存储到rightArr中 ,在其他地方使用 _this.setData({ rightArr:heightArr }) }) }, 500) }, // 在onShow生命周期中请求数据 onShow(){ // 使用云函数请求数据 let _this = this; wx.cloud.callFunction({ name:"getMenuData" }).then(res=>{ // console.log(res.result.data[0].arr); _this.setData({ menuArr:res.result.data[0].arr }) // 调用方法, 在请求到数据之后在获取高度,保证获取到的高度是正确的 this.getBoxH(); }) } }) //这样还存在一个问题: // 就是在点击左侧倒数第一项时,会闪跳到第二项,主要是因为左侧倒数第二项对应的右侧区块高度在右侧 // 倒数第一和倒数第二项区块高度和之间,因此选中了倒数第二项 // onShow(){} 和 onLoad(){}生命周期的区别 // onShow 当小程序切换到后台后被隐藏,再次进入小程序会在执行一次,如果要每次进入该页面时要请求数据可以在这个生命周期中 // onLoad 是页面只加载一次,切换到后台在切换回来不会再请求数据