Use flex to implement waterfall flow

foreword

Hello everyone, this is the Fujiwara Tofu shop. I said I would write an article to record the realization of the waterfall flow, but it has been delayed until now. I will make up for it when I have time in the afternoon.

what is cascade

Waterfall flow, also known as waterfall flow layout. It is a relatively popular website page layout, and its visual performance is an uneven multi-column layout. As the page scroll bar scrolls down, this layout will continue to load data blocks and append them to the current tail.

Features:

  • fixed width, variable height
  • spotty layout

Use flex to implement waterfall flow

The effect achieved is a waterfall flow divided into two columns, and sliding down will load the data of the next page and load it into the page

Style implementation

<view class="blessing-con">
  <view class='blessing-con-half'>
    <view id="leftHalf">
      <view class="blessing-con-half-item" :class="bgColor[index % 3]"
        v-for="(item, index) in newBlessingWordsList1" :key="index">
        <view class="item-con">
        </view>
      </view>
    </view>
  </view>
  <view class='blessing-con-half'>
    <view id="rightHalf">
      <view class="blessing-con-half-item" :class="bgColor[(index + 1) % 3]"
        v-for="(item, index) in newBlessingWordsList2" :key="index">
        <view class="item-con"></view>
      </view>
    </view>
  </view>
</view>
<view class="blessing-more" @click="handlerMore">
  <image v-if="hasWallNext" class="more-icon"
    src="xx/blessingGame/arr-down.png">
  </image>
  <view class="blessing-more-text">{{ blessingStatus }}</view>
</view>

.blessing-con defines the outer container as a flex layout, and sets the main axis alignment to space-between

.blessing-con-half defines the style of the container on the left and right sides

.blessing-con-half-item defines the style of each small box

.blessing-con {
  padding: 32rpx 20rpx;
  display: flex;
  justify-content: space-between;
  height: 1100rpx;
  overflow-y: auto;
  .blessing-con-half {
    width: 320rpx;
    height: 100%;
    box-sizing: border-box;
    .blessing-con-half-item {
      width: 100%;
      height: auto;
      display: flex;
      flex-direction: column;
      box-sizing: border-box;
      margin: 0 0 24rpx;
      position: relative;
    }
  }
}

Here, the background color of each small box is in the order of blue-yellow-red, and a jagged image is added to the top of the box through a pseudo-class to achieve a jagged effect

bgColor: ['blueCol', 'yellowCol', 'pinkCol'], //祝福墙背景
// 不同颜色
.blessing-con-half-item {
    &.pinkCol {
      &::before {
        .setbg(320rpx, 16rpx, 'blessingGame/pink-bg.png');
      }
      .item-con {
        background: #FFE7DF;
      }
    }
  
    &.yellowCol {
      &::before {
        .setbg(320rpx, 16rpx, 'blessingGame/orange-bg.png');
      }
      .item-con {
        background: #fff0e0;
      }
    }
  
    &.blueCol {
      &::before {
        .setbg(320rpx, 16rpx, 'blessingGame/blue-bg.png');
      }
      .item-con {
        background: #e0f7ff;
      }
    }
  }
}

function realization

Define two arrays in data to store the data of the left and right lists

data(){
  return{
    blessingWordsList: [],// 祝福墙数据
    newBlessingWordsList: [],
    newBlessingWordsList1: [],//左列表
    newBlessingWordsList2: [],//右列表
    isloading:false,//是否正在加载
    hasWallNext:false,//是否有下一页
    leftHeight: 0,//左高度
    rightHeight: 0,//右高度
    blessingWordsCount: 0,//计数器
    isActive: 0, //tab初始化索引
    timer:null,//定时器
  }
}

Call interface request list data

  • The first request for data needs to initialize list data and counters
  • The timer needs to be started after each request
// 获取祝福墙列表(type=1则请求下一页)
  async getBlessingWall(type = 0) {
    try {
      let res = await api.blessingWall({
        activityId: this.activityId,
        pageNum: this.pageWallNum,
        pageSize: this.pageWallSize
      })
      this.isloading = false
      if (res.code == 1 && res.rows) {
        let list = res.rows
        this.blessingWordsList = (type==0 ? list : [...this.blessingWordsList, ...list])
        if (this.blessingWordsList.length > 0 && !this.timer && this.isActive == 1) {
            if(this.pageWallNum == 1){
                    this.newBlessingWordsList = []
                    this.newBlessingWordsList1 = []
                    this.newBlessingWordsList2 = []
                    this.blessingWordsCount = 0
            }
            this.start()
        }
        // 处理请求下一页的情况
        if (type == 1) {
          this.start()
        }
        this.hasWallNext = res.hasNext
        if (!this.hasWallNext) {
          this.blessingStatus = "没有更多了哟"
        } else {
          this.blessingStatus = "点击加载更多"
        }
      }
    } catch (error) {
      console.log(error)
    }
  },
// 加载更多
 async handlerMore() {
  if (this.hasWallNext && !this.isloading) {
    this.isloading = true
    this.pageWallNum++
     await this.getBlessingWall(1)
  }
},

Start a timer to dynamically add data to the left and right lists

start() {
  // 清除定时器
  clearInterval(this.timer)
  this.timer = null;
  
  this.timer = setInterval(() => {
    let len = this.blessingWordsList.length
    if (this.blessingWordsCount < len) {
      let isHave = false
      // 在列表中获取一个元素
      let item =this.blessingWordsList[this.blessingWordsCount]
      // 判断新列表中是否已经存在相同元素,防止重复添加
      this.newBlessingWordsList.forEach((tmp)=>{
        if(tmp.id == item.id){
          isHave = true
        }
      })
      // 如果不存在
      if (!isHave) {
        this.newBlessingWordsList.push(item)//添加该元素
        this.$nextTick(() => {
          this.getHei(item)//添加元素到左右列表
        })
      }
    } else {
      // 遍历完列表中的数据,则清除定时器
      clearInterval(this.timer)
      this.timer = null;
    }
  }, 10)
}

Calculate the height of the current left and right containers, and determine which side the data should be added to

  • Use the uni-app method to get the dom objects of the left and right containers, and then get their current heights
  • Compare the left and right heights and dynamically insert data into the two arrays
  • Each time a piece of data is inserted, the counter +1
getHei(item) {
  const query = uni.createSelectorQuery().in(this)
  // 左边
  query.select('#leftHalf').boundingClientRect(res => {
    if (res) {
      this.leftHeight = res.height
    }
    // 右边
    const query1 = uni.createSelectorQuery().in(this)
    query1.select('#rightHalf').boundingClientRect(dataRight => {
      if (dataRight) {
        this.rightHeight = dataRight.height != 0 ? dataRight.height : 0
        if (this.leftHeight == this.rightHeight || this.leftHeight < this.rightHeight) {
          // 相等 || 左边小  
          this.newBlessingWordsList1.push(item)
        } else {
          // 右边小
          this.newBlessingWordsList2.push(item)
        }
      }
      this.blessingWordsCount++
    }).exec()
  }).exec()
},

There is a point to note here. When calling the start method, you must ensure that the page renders the elements of the left and right containers, otherwise you will not get the height of the container.

For example, my project has tab switching

When entering the page, data will be requested once. At this time, because the initial state of the tab is 0, the start method will not be called. When the tab is switched to 1, the start method will be called to start calculating the height.

data(){
  return{
    isActive: 0, //tab初始化索引
    timer:null,//定时器
  }
}
async onLoad(options) {
  this.getBlessingWall()
}
// tab选项卡切换
tabClick(index) {
  this.isActive = index
  this.isLoaded = false;
  if (this.blessingWordsList.length > 0 && !this.timer && this.isActive == 1) {
    if(this.pageWallNum == 1){
      this.newBlessingWordsList = []
      this.newBlessingWordsList1 = []
      this.newBlessingWordsList2 = []
      this.blessingWordsCount = 0
    }
    this.start()
  }
},

at last

This time I chose flex to realize the waterfall flow. There are several other ways to realize the waterfall flow. If there is a chance later, I will add several other methods. If you are interested, you can pay attention!

Guess you like

Origin juejin.im/post/7260713996165021754