1. 程式人生 > 實用技巧 >微信小程式---scroll-view實現左右兩側聯調

微信小程式---scroll-view實現左右兩側聯調

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;
  /* width: 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 是頁面只加載一次,切換到後臺在切換回來不會再請求資料