使用flex实现瀑布流

前言

大家好,这里是藤原豆腐店,之前说要写一篇文章来记录瀑布流的实现,一直拖到了现在,趁下午有空,赶紧补上。

什么是瀑布流

瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。

特点:

  • 固定宽度,高度不一
  • 参差不齐的布局

使用flex实现瀑布流

实现的效果是分成两列的瀑布流,往下滑动会加载下一页的数据,并加载到页面中

样式的实现

ini 复制代码
<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 定义外层容器为flex布局,同时设置主轴对齐方式为space-between

.blessing-con-half定义左右两侧的容器的样式

.blessing-con-half-item定义每一个小盒子的样式

css 复制代码
.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;
    }
  }
}

这里每个小盒子的背景色按蓝-黄-红的顺序,同时通过伪类给盒子顶部添加锯齿图片,实现锯齿效果

arduino 复制代码
bgColor: ['blueCol', 'yellowCol', 'pinkCol'], //祝福墙背景
less 复制代码
// 不同颜色
.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;
      }
    }
  }
}

功能实现

在data中定义两个数组存储左右列表的数据

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

调取接口请求列表数据

  • 第一次请求数据需要初始化列表数据,计数器
  • 每一次请求完都需要开启定时器
kotlin 复制代码
// 获取祝福墙列表(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)
  }
},

开启一个定时器,用于动态添加左右列表的数据

kotlin 复制代码
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)
}

计算当前左右容器的高度,判断数据要添加到哪一边

  • 使用uni-app的方法获取左右容器的dom对象,再获取他们当前的高度
  • 比较左右高度,向两个数组动态插入数据
  • 每插入一条数据,计数器+1
kotlin 复制代码
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()
},

这里有一个注意点,调用start方法的时候,必须确保页面渲染了左右容器的元素,否则会拿不到容器的高度

比如我这个项目是有tab切换的

进入页面的时候会请求一次数据,这时候因为tab初始状态在0,所以并不会调用start方法,要到切换tab到1时,才会调用start方法开始计算高度。

kotlin 复制代码
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()
  }
},

最后

这次选用了flex实现瀑布流,实现瀑布流的方式还有其他几种方法,后续有机会的话,我会补充其他几种方式,如果感兴趣的话,可以点点关注哦!

相关推荐
喵叔哟20 分钟前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特1 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解1 小时前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django