实现瀑布流布局的四种方法

这篇具有很好参考价值的文章主要介绍了实现瀑布流布局的四种方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、 什么是瀑布流布局

实现瀑布流布局的四种方法

1.是什么

  • 页面上是一种 参差不齐 的多栏布局,类似上图所示随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部,大部分为图片,图片 固定 宽度,高度 不一,根据原比例缩放到宽度达到固定的要求,每行排满后,新的图片添加到后面

2.特点

  • 固定宽度,高度不一
  • 岑参不齐的布局
  • 以图片为主

二、有什么优缺点

1.优点

  • 节省空间:降低页面的复杂
  • 对于 触屏设备非常友好:通过向上滑动浏览,交互方式更符合直觉
  • 良好的视觉体验:浏览时不会被页面整整齐齐的高度影响,参差不齐,降低浏览的疲劳

2.缺点

  • 内容总长度 无法掌握
  • 数据过多时,容易造成页面 加载的负荷
  • 再次加载时 很难定位上一次浏览的内容

三、实现方法

法一、纯css的写法:【multi-column 多栏布局】

实现瀑布流布局的四种方法

1.两个重要属性

column-count : 定义列数
column-gap :列与列之间的间隔

2.特点

顺序只能是 从上到下 再左到右

3.缺点

由于排列顺序是先 从上到下 再左到右,只能用于数据固定, 无法动态的加载追加,对于滚动到底部加载新数据则无法实现。

4.注意点:

有时候页面会出现在前几列的最后一个元素的内容被自动断开,一部分在当前列尾,一部分在下一列的列头。这时候子元素可以用 break-inside设置为不被截断 avoid来控制

break-inside: avoid; // 不被截断 默认值是auto,会被截断

5.实现的代码模式(以下用vue3.0来书写)

  • template
<template>
  <div  class="page-main">
    <div class="card">
        <div class="card-item" v-for="(item,index) in cardList" :key="index" :style="[{background: item.color},{height: item.height},{lineHeight: item.height}]">
             <p class="text">{{index}}</p>
        </div>
    </div>
  </div>
</template>
  • script
<script setup>
import {ref} from 'vue'
const cardList = ref([ // 模拟数据
  {
    color:  '#FCCF0A',
    height: '100px',
  },
  ......
])
</script>
  • style multi-column 实现
<style lang="scss" scoped>
.page-main{
    background: #ffffff;
    min-height:100vh;
    padding: 0 30px;
    .card{
        column-count: 3; // 定义三列
        column-gap: 20px; // 列与列的距离为20px
      .card-item{
        text-align: center;
        width: 216px;
        border-radius: 16px;
        grid-row-start: auto;
        margin-bottom: 20px;
        break-inside: avoid; // 不被截断
      }
    }
}
</style>

法二、泳道的思想:【用flex弹性布局+计算元素高度实现布局】

泳道的概念:通俗的说是类似泳道一样,先设置泳道列数,一列一列的,然后往里加东西,就自动往下面走了。

1.原理的分析

  • 适用范围:基本业务都能实现
  • 首先比如有四个元素了,并且设置四列

实现瀑布流布局的四种方法

  • 那么第五个位置在哪里呢

实现瀑布流布局的四种方法

  • 答案: 是下面图的位置上,找到的位置应该高度为所有列中最小的位置

实现瀑布流布局的四种方法

  • 第六个的位置呢,答案还是和上面一样的所示 找到高度为所有列中最小的位置,则为下面的位置
    实现瀑布流布局的四种方法

2.算法思路:通过上面的分析则能了解瀑布流的思路了

  • 设计要分成的列数
  • 设置每列的 宽度一致
  • 每次插入的位置选择所有列高度最小 的位置,依次循环

3.代码实现思路

  • 由于需要拿到每个数据的dom元素的实际值,则需要先对数据进行赋值,这样就可以拿到所有的元素dom上的高度
  • 则渲染时候对数据的两次赋值,则会出现一次闪现,需要防抖 ,可以先将卡片设置为visibility:hidden ,当后面对数据处理完,重新渲染后再将卡片设置为visibility:visible
  • 多少列则定义多少新的空数组,然后根据瀑布流的思路依次插入 到空的数组即可,最后再重新渲染页面即可

4.代码实现(以下用vue3.0来实现)

实现瀑布流布局的四种方法

  • template (例子为三列)
<template>
  <div  class="page-main">
    <div class="card">
        <div class="coloum1" >
          <div class="card-item" v-for="(item,index) in cardList1" :key="index" :style="[{background: item.color},{height: item.height},{lineHeight: item.height}]" :class="{visibles: isVisibility}">
              <p class="text">{{item.num}}</p>
          </div>
        </div>
        <div class="coloum2">
           <div class="card-item" v-for="(item,index) in cardList2" :key="index" :style="[{background: item.color},{height: item.height},{lineHeight: item.height}]" :class="{visibles: isVisibility}">
              <p class="text">{{item.num}}</p>
          </div>
        </div>
         <div class="coloum3">
           <div class="card-item" v-for="(item,index) in cardList3" :key="index" :style="[{background: item.color},{height: item.height},{lineHeight: item.height}]" :class="{visibles: isVisibility}">
              <p class="text">{{item.num}}</p>
          </div>
        </div>
    </div>
  </div>
</template>
  • js
<script setup>
import {ref,onMounted, reactive,nextTick} from 'vue'
const cardList = reactive([ // 测试数据
  {
    num: '0',
    color:  '#FCCF0A',
    height: '100px',

  }, 
  ...测试数据
])

const isVisibility = ref(true)
// 由于渲染时候对数据的两次赋值,则会出现一次闪现,需要防抖

onMounted(()=>{
  equallyCard()
  nextTick(()=>{
    caLFlex()
  }).then(()=>{
    isVisibility.value = true
  })
})

const cardList1 = ref([]) // 各列的数据
const cardList2 = ref([])
const cardList3 = ref([])

function equallyCard(){
  // 平分数据,确保页面上遍历卡片dom的真实顺序与平分的一致 document.querySelectorAll('.card-item'))
  let num = parseInt(cardList.length/3)
  cardList.forEach((item,index)=>{
    if(index<num){
       cardList1.value.push(item)
       return
    }
    if(index<2*num){
       cardList1.value.push(item)
        return
    }
    cardList3.value.push(item)
  })
}


function caLFlex(){
  let arr1 = [] // 第一列的值
  let arr2 = [] // 第二列的值
  let arr3 = [] // 第二列的值
  let heightArry_1 = [] // 第一列的卡片高度
  let heightArry_2 = [] // 第二列的卡片高度
  let heightArry_3 = [] // 第二列的卡片高度
  Array.from(document.querySelectorAll('.card-item')).forEach((item,index) =>{
      if(index === 0){ // 第一行中的元素无需判断,直接加到新的数组中
          heightArry_1.push(item.offsetHeight)
          arr1.push(cardList[index])
          return
      }
      if(index === 1){
          heightArry_2.push(item.offsetHeight)
           arr2.push(cardList[index])
           return
      }
      if(index === 2){
          heightArry_3.push(item.offsetHeight)
          arr3.push(cardList[index])
          return
      }
     const heightTotal_1 = heightArry_1.length ? Array.from(heightArry_1).reduce(( acc, cur ) => acc + cur) : 0 // 第一列的总高度
     const heightTotal_2 = heightArry_2.length ? Array.from(heightArry_2).reduce(( acc, cur ) => acc + cur) : 0 // 第二列的总高
     const heightTotal_3 = heightArry_3.length ? Array.from(heightArry_3).reduce(( acc, cur ) => acc + cur) : 0 // 第三列的总高度

     // 找到最小值
     let minNumber = Math.min(heightTotal_1,heightTotal_2,heightTotal_3)
     switch (minNumber) {
       case heightTotal_1:
          heightArry_1.push(item.offsetHeight)
          arr1.push(cardList[index])
         break
      case heightTotal_2:
         heightArry_2.push(item.offsetHeight)
          arr2.push(cardList[index])
         break
      case heightTotal_3:
         heightArry_3.push(item.offsetHeight)
          arr3.push(cardList[index])
         break
     }
  })

// 重新将数据赋值给各列数组
  cardList1.value = arr1
  cardList2.value = arr2
  cardList3.value = arr3
}
  • css
<style lang="scss" scoped>
.page-main{
    background: #ffffff;
    height:100vh;
    overflow: hidden;
    padding: 0 30px;
    .card{
        display: flex;
        flex-direction: row;
        justify-content: space-around;
        .card-item{
          visibility: hidden;
          margin-bottom: 20px;
          text-align: center;
          width: 216px;
          border-radius: 16px;
        }
        .visibles {
          visibility: visible;
        }
    }
}
</style>

法三、绝对定位实现:【精确计算每个子元素绝对定位到瀑布流它应该去的地方,需要后期一些优化,并不推荐使用】

1.缺点

  • 计算量相比较,较复杂
  • 会有高度塌陷问题
  • 子元素因为设置了 absolute并不会占高,页面可滚动的话又会产生另外的问题
  • 如果在移动端中会做适配,当前的绝对定位的高度单位在代码实现若为px 并不会自动换算
  • 当视口的窗口resize改变,需要重新计算元素的位置,若会不断触发事件,性能消耗大,加载也慢,这是不可取的

2.实现的原理

  • 通过>精准的计算每个子元素的位置,定位到瀑布流应该去的地方

3.代码实现思路

  • 子元素全部设置成绝对定位
  • 找到所有列中>最小的位置
  • 计算子元素定位时的 top 以及left
  • 修改子元素的样式,设置position为absolute ,以及设置top ,left
  • 每次插入的位置选择所有列高度最小 的位置,依次循环

4.代码实现(vue3.0来实现)

实现瀑布流布局的四种方法

  • template
<template>
  <div  class="page-main">
    <div class="card">
          <div class="card-item" v-for="(item,index) in cardList" :key="index" :style="[{background: item.color},{height: item.height},{lineHeight: item.height}]" :class="{visibles: isVisibility}">
              <p class="text">{{item.num}}</p>
          </div>
    </div>
  </div>
</template>
  • script

以下是一些代码说明
1、 coloumHight [0,0] 为两列,coloumHight [0,0,0] 为三列
2、 getMinColoumHeight方法: 找到最小列

<script setup>
import {onMounted, reactive, ref} from 'vue'
const cardList = reactive([ // 测试数据
  {
    num: '0',
    color:  '#FCCF0A',
    height: '100px',

  },
  ...测试数据
])

const isVisibility = ref(false)
// 由于渲染时候对数据的两次赋值,则会出现一次闪现,需要防抖

onMounted(()=>{
    caLFlex()
})

function caLFlex(){
  let coloumHight = [0,0,0] // 每列元素的高度,本例子为3列

// 依次插入每个元素
 Array.from(document.querySelectorAll('.card-item')).forEach((item,index) =>{
   let coloum = getMinColoumHeight(coloumHight) // 得到当前所有最小高度的一列
   let itemTop = coloumHight[coloum]
   let itemLeft = coloum * 200
   item.style.position = "absolute"
   item.style.top = `${itemTop}px`
   item.style.left = `${itemLeft}px`

  //  当前高度加上新增的元素高度
  coloumHight[coloum] += item.offsetHeight
 })

 isVisibility.value = true
}

// 找到所有列中高度最小的一列
function getMinColoumHeight(arr){
   let min = Math.min(...arr)
   return arr.indexOf(min) !== -1 ? arr.indexOf(min) : 0  //  默认第一列
}
</script>
  • style
<style lang="scss" scoped>
.page-main{
    background: #ffffff;
    height:100vh;
    overflow: hidden;
    padding: 0 30px;
    .card{
      position: relative;
      .card-item{
        width: 200px;
        text-align: center;
        visibility: hidden;
      }
      .visibles {
          visibility: visible;
      }
    }
}
</style>

法四、插件实现:【用插件vue-waterfall2 不是很建议使用,一般用于图片的加载,但是复杂的业务就不是很ok了】

1.安装依赖

npm i vue-waterfall2@latest --save

2.引入

import waterfall from 'vue-waterfall2'
Vue.use(waterfall)

3.相关api

实现瀑布流布局的四种方法
实现瀑布流布局的四种方法文章来源地址https://www.toymoban.com/news/detail-414475.html

4.代码实现

<template>
    <div class="container-water-fall">
        <waterfall
            :col="col"
            :data="data"
            @scroll="scroll"
            @finish="finish"
            :height="'100vh'"
        >
            <template>
                <div
                    class="cell-item"
                    v-for="(item, index) in data"
                    :key="index"
                >
                    <img v-if="item.img" :src="item.img" alt="加载错误" />
                    <div class="item-body">
                        <div class="item-desc">{{ item.title }}</div>
                        <div class="item-footer">
                            <div
                                v-if="item.avatar"
                                class="avatar"
                                :style="{
                                    backgroundImage: `url(${item.avatar})`,
                                }"
                            ></div>
                            <div class="name">{{ item.user }}</div>
                            <div
                                class="like"
                                :class="item.liked ? 'active' : ''"
                            >
                                <i></i>
                                <div class="like-total">{{ item.like }}</div>
                            </div>
                        </div>
                    </div>
                </div>
            </template>
        </waterfall>
    </div>
</template>





<script>
import json from "./components/data.json";
export default {
    name: "App",
    /*
  注意:
  1. gutterWidth需要与width一起使用才会生效,否则会进行自适应宽度(使用rem布局时,需先计算出自适应后的宽度再传值)
  2. 使用了`waterfall`的父组件,如果样式存在问题,可去掉css `scoped`尝试一下
*/
    data() {
        return {
            data: [
                {
                    img:
                        "https://image.watsons.com.cn//upload/8a316140.png?w=377&h=451&x-oss-process=image/resize,w_1080",
                    avatar:
                        "https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
                    title: "最近浴室新宠,日系神仙好物了解一下~",
                    user: "迷人的小妖精迷人的小妖精",
                    like: "953",
                },
                {
                    img:
                        "https://image.watsons.com.cn//upload/5c3e51e4.jpg?w=720&h=960&x-oss-process=image/resize,w_1080",
                    avatar:
                        "https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
                    title: "真的是万能.超级实用.包包必备单品! ! !",
                    user: "迷人的小妖精迷人的小妖精",
                    like: "952",
                },
                {
                    img:
                        "https://image.watsons.com.cn//upload/bef41e67.JPG?w=712&h=534&x-oss-process=image/resize,w_1080",
                    avatar:
                        "https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
                    title: "150元搞定全套护肤品,这些护肤好物必须交出来!",
                    user: "迷人的小妖精迷人的小妖精",
                    like: "953",
                },
                {
                    img:
                        "https://image.watsons.com.cn//upload/13afe9a7.jpg?x-oss-process=image/resize,w_1080",
                    avatar:
                        "https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
                    title: "夏天用这款姨妈巾,让你体验真正的清爽",
                    user: "迷人的小妖精迷人的小妖精",
                    like: "953",
                },
                {
                    img:
                        "https://image.watsons.com.cn//upload/642cb83c.jpeg?w=1080&h=1080&x-oss-process=image/resize,w_1080",
                    avatar:
                        "https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
                    title:
                        "贵妇级好用的水乳有哪些呢?千万不要去乱尝试贵妇级好用的水乳有哪些呢?千万不要去乱尝试贵妇级好用的水乳有哪些呢?千万不要去乱尝试",
                    user: "迷人的小妖精迷人的小妖精",
                    like: "953",
                },
                {
                    img:
                        "https://image.watsons.com.cn//upload/98c7c4c3.jpg?w=1210&h=1210&x-oss-process=image/resize,w_1080",
                    avatar:
                        "https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
                    title:
                        "贵妇级好用的水乳有哪些呢?千万不要去乱尝试贵妇级好用的水乳有哪些呢?千万不要去乱尝试贵妇级好用的水乳有哪些呢?千万不要去乱尝试",
                    user: "迷人的小妖精迷人的小妖精",
                    like: "953",
                },
                {
                    img:
                        "https://image.watsons.com.cn//upload/25ab20fe.JPG?w=1000&h=1200&x-oss-process=image/resize,w_1080",
                    avatar:
                        "https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
                    title: "夏天用这款姨妈巾,让你体验真正的清爽",
                    user: "迷人的小妖精迷人的小妖精",
                    like: "953",
                },
                {
                    img:
                        "https://image.watsons.com.cn//upload/083767f0.JPG?w=828&h=620&x-oss-process=image/resize,w_1080",
                    avatar:
                        "https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
                    title: "150元搞定全套护肤品,这些护肤好物必须交出来!",
                    user: "迷人的小妖精迷人的小妖精",
                    like: "953",
                },
            ],
            col: 2,
        };
    },
    computed: {
        itemWidth() {
            return 138 * 0.5 * (document.documentElement.clientWidth / 375); //rem布局 计算宽度
        },
        gutterWidth() {
            return 9 * 0.5 * (document.documentElement.clientWidth / 375); //rem布局 计算x轴方向margin(y轴方向的margin自定义在css中即可)
        },
    },
    methods: {
        scroll(scrollData) {
            console.log(scrollData);
        },
        switchCol(col) {
            this.col = col;
            console.log(this.col);
        }
    },
};
</script>

<style>
* {
    margin: 0;
}
.cell-item {
    width: 100%;
    margin-bottom: 10px;
    background: #ffffff;
    border: 2px solid #f0f0f0;
    border-radius: 12px 12px 12px 12px;
    overflow: hidden;
    box-sizing: border-box;
}
img {
    width: 100%;
    height: auto;
    display: block;
}
</style>


到了这里,关于实现瀑布流布局的四种方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信小程序css实现瀑布流布局

    废话不多说 直接上代码 博客地址:BULINGBULING

    2024年02月13日
    浏览(44)
  • 微信小程序实现吸顶、网格、瀑布流布局

    微信小程序开发通常是在webview模式下编写,但是对小程序的渲染性能有一定的追求,就需要使用Skyline模式进行渲染,同时在这种模式下有也有一些特殊的组件,可以轻松的实现想要的效果,本文将介绍在Skyline模式下如何实现吸顶、网格、瀑布流布局。 以下是具体的实现:

    2024年02月22日
    浏览(60)
  • 微信小程序实现瀑布流布局(方式一)

    根据奇数列和偶数列区分左边和右边数据 设置width固定,mode=“widthFix” 适用于:左右两列的高度相差不是很大

    2024年02月12日
    浏览(39)
  • 有关实现深拷贝的四种方法

            深拷贝与浅拷贝:         在开始之前我们需要先了解一下什么是浅拷贝和深拷贝,其实深拷贝和浅拷贝都是针对的引用类型,JS中的变量类型分为值类型(基本类型)和引用类型;对值类型进行复制操作会对值进行一份拷贝,而对引用类型赋值,则会进行地址

    2024年02月04日
    浏览(42)
  • Jmeter实现参数化的四种方法

    目录 一、CSV Data Set Config/CSV数据文件设置 二、函数助手 1、Random函数 2、RandomString随机字符串 3、CSVRead 4、time 5、UUID 三、计数器(Counter)         参数化:自动化测试脚本的一种常用技巧,事先准备好数据,指定参数的取值范围和规则;而非在脚本中写死,脚本执行时从

    2024年04月22日
    浏览(39)
  • 【Vue】实现当前页面刷新的四种方法

    这两周在写一个后台管理,每次调用接口实现增删改查的过程中,都需要刷新当前页面或者刷新数据。如果手动点击浏览器的小圈圈不仅麻烦、用户体验感极差,而且不会真的有人让用户手动刷新叭。。。这个问题可以称得上是前端的bug了。那么,顺着这个问题,一通搜寻下

    2023年04月26日
    浏览(54)
  • CSS实现三角形的四种方法

    【解释】不设置宽高,用边框大小控制三角型大小 【分解步骤】 设置一个 div 不设宽高 【示例】   2. 设置透明 留下想要指向方向 相反 的边框设定,其他方向的边框设为 transparent 透明 【示例】 实现指向向上的三角形 【效果图】 指向上,指向下,指向左,指向右   如何设

    2024年02月15日
    浏览(46)
  • Android响应事件onClick方法的四种实现方式

    在Android的开发中,对于点击事件的OnClickListener有下面四种实现方式,可以根据实际场景的需要选择合适的用法。下面以Button按钮来举例说明。 适合场景:任何场景都通用,但对于一个Activity中要是有多个控件要实现onClick方法就会显得代码冗余。 同方法一,两者差别不大 适合

    2024年02月13日
    浏览(51)
  • SpringBoot项目启动后执行指定方法的四种实现

    今日的好天气 距离上一次更新帖子已经过了很久很久,久到我也不知道我在搞什么飞机。 国庆节第一天终于搬到了新家,最近量子纠缠比较火,冬天也在路上,匆匆又一年。 @PostConstruct是Java自带的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在

    2023年04月15日
    浏览(44)
  • 【数据结构】-快速排序的四种方法实现以及优化

    作者:小树苗渴望变成参天大树 作者宣言:认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧! 今天讲一种不一样的排序,听名字就知道这个排序不拐弯抹角的,我们来看看它又多快速,并且快速排序的前三种方法都是递归思想,

    2024年02月03日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包