瀑布流布局原理

基本思路

假设一排放5张图片。当第一排排满足够多的等宽的元素时,显示的是这样的。那么假如我们要放第6个元素的时候,应该放在什么位置呢? 如果按照我们的正常逻辑来想,应该是放在第一张图片下面,依次水平排列过去,如下图:

这样布局会出现列的长短不一,实现不了瀑布流的效果,正确的思路应该是从第6个元素开始,接下去的每一个元素都会放在上行中高度最低的那一列下方。如下图:

代码实现

ini 复制代码
import { useEffect } from 'react';
import styles from './style.module.scss'
import classNames from 'classnames';

const list = [
  {
 img:'https://img0.baidu.com/it/u=2269054305,2051617662&fm=253&fmt=auto&app=138&f=JPEG?w=347&h=459',
    title:'这是标题这是标题',
    des:'这是描述一一一一一一'
  },
  {
 img:'https://img1.baidu.com/it/u=1569101010,2354561158&fm=253&fmt=auto&app=138&f=JPEG?w=477&h=500',
    title:'这是标题这是标题',
    des:'这是描述这是描述这是描述这是描述这是描述这是描述二二二二二二二二二二'
  },
  {
   img:'https://img0.baidu.com/it/u=546803172,3230568524&fm=253&fmt=auto&app=138&f=PNG?w=427&h=500',
    title:'这是标题这是标题',
    des:'这是描述这是描述这是描述这是描述这是描述这是描述这是描述这是描述这是描述这是描述这是描述这是描述'
  },
  {
    img:'	https://img1.baidu.com/it/u=1937457859,1823693439&fm=253&fmt=auto&app=138&f=JPEG?w=456&h=455',
    title:'这是标题这是标题',
    des:'这是描述这是描述这是描述这是描述三三'
  },
  {
 img:'https://img0.baidu.com/it/u=1440396978,4151905066&fm=253&fmt=auto&app=138&f=JPEG?w=388&h=556',
    title:'这是标题这是标题',
    des:'这是描述这是描述这是描述这是描述这是描述这是描述这是描述这是描述这是描述这是描述这是描述这是描述'
  },
  {
  img:'https://img2.baidu.com/it/u=3300693751,3848672578&fm=253&fmt=auto&app=138&f=PNG?w=500&h=754',
    title:'这是标题这是标题',
    des:'这是描述这是描述这是描述这是描述这是描述'
  },
  {
 img:'https://img0.baidu.com/it/u=3573637456,2237837126&fm=253&fmt=auto&app=138&f=JPEG?w=268&h=347',
    title:'这是标题这是标题',
    des:'这是描述这是描述这是描述这是描述这是描述这是描述四四四这是描述这是描述这是描述这是描述这是描述这是描述四四四'
  },
  {
 img:'https://img1.baidu.com/it/u=2908802412,1187745874&fm=253&fmt=auto&app=120&f=JPEG?w=196&h=228',
    title:'这是标题这是标题',
    des:'这是描述这是描述这是描述这是描述这是描述'
  },
]
const Home = () => {
  // 每一行显示5个元素
  const column = 5;
  // 间距
  const gap = 20;
  // 每列高度数组
  const heightList:number[] = [];
  useEffect(()=>{
    getElementPosition();
    setListHeight();
  },[])
  const getElementPosition = ()=>{
    const items = document.querySelectorAll('.item');
    const iWidth = (window.innerWidth - 40 - (column - 1) * gap) / column;
    for(let index = 0; index < items.length; index ++){
      const item = items[index];
      const height = items[index].clientHeight;
      // 设置第一排元素样式,记录每一列高度
      if (index < column) {
        heightList[index] = height;
        item.setAttribute(
          "style",
          `width: ${iWidth}px;transform: translate(${index*iWidth+index*gap}px,0)`
        )
      // 后面每次向高度最低的列加入数据
      }else{
        let minHeight :number = heightList[0];
        let minIndex :number = 0;
        // 找到最小高度元素,向下插入元素
        for (let j = 0; j < heightList.length; j++) {
          if (minHeight > heightList[j]) {
            minHeight = heightList[j];
            minIndex = j;
          }
        }
        // 设置插入元素位置
        item.setAttribute(
          "style",
          `width:${iWidth}px;transform:translate(${(iWidth + gap) * minIndex}px,${minHeight + gap}px);`
        );
        // 更新最小高度
        heightList[minIndex] = height + gap + minHeight;
      }
    }
  }
  const setListHeight = ()=>{
    const max = Math.max(...heightList);
    const list = document.getElementById('list');
    if(list){
      list.style.height = `${max}px`;
    }
  }
  return (
    <div className={styles.home}>
      <div className={classNames(styles.list)} id='list'>
        {
          list.map((n,index)=>{
            return <div className={classNames(styles.item,"item")} key={index} 
             
            >
              <div className={styles['img-wrap']}>
                <img src={n.img} alt="" />
              </div>
              <div className={styles.title}>{n.title}</div>
              <div className={styles.des}>{n.des}</div>
            </div>
          })
        }
      </div>
    </div>
  );
};

export default Home;
css 复制代码
// style.module.scss
.home{
  padding: 20px 30px;
  background: #fff;
  .list {
    width: 100%;
    position: relative;
    .item{
      position: absolute;
      left: 0;
      top: 0;
      border:1px solid #eee;
      padding: 10px;
      box-sizing: border-box;
      .img-wrap{
        width: 100%;
        img{
          width: 100%;
          height: auto;
        }
      }
      .title{
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
    }
  }
}

效果如图:

相关推荐
码是生活几秒前
鸿蒙开发排坑:解决 resourceManager.getRawFileContent() 获取文件内容为空问题
前端·harmonyos
婷婷婷婷4 分钟前
v-copyText 自定义指令 —— 复制文本内容
前端
waylon111136 分钟前
【HOC】高阶组件在Vue老项目中的实战应用 - 模块任意排序
前端·vue.js·面试
阳阳羊7 分钟前
Mpx 动画
前端
编程社区管理员7 分钟前
「2025最新版React+Ant Design+Router+TailwindCss全栈攻略:从零到实战,打造高颜值企业级应用
前端·react.js·前端框架
DJA_CR7 分钟前
解决在 TSX 中使用 `RouterView` + `KeepAlive` 不生效问题
前端·vue.js
前端爆冲18 分钟前
项目中无用export的检测方案
前端
热爱编程的小曾1 小时前
sqli-labs靶场 less 8
前端·数据库·less
gongzemin1 小时前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
Apifox1 小时前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd