瀑布流布局原理

基本思路

假设一排放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;
      }
    }
  }
}

效果如图:

相关推荐
bin91531 小时前
DeepSeek 助力 Vue 开发:打造丝滑的复制到剪贴板(Copy to Clipboard)
前端·javascript·vue.js·ecmascript·deepseek
晴空万里藏片云3 小时前
elment Table多级表头固定列后,合计行错位显示问题解决
前端·javascript·vue.js
曦月合一3 小时前
html中iframe标签 隐藏滚动条
前端·html·iframe
奶球不是球3 小时前
el-button按钮的loading状态设置
前端·javascript
kidding7233 小时前
前端VUE3的面试题
前端·typescript·compositionapi·fragment·teleport·suspense
Σίσυφος19005 小时前
halcon 条形码、二维码识别、opencv识别
前端·数据库
学代码的小前端5 小时前
0基础学前端-----CSS DAY13
前端·css
css趣多多6 小时前
案例自定义tabBar
前端
姑苏洛言7 小时前
DeepSeek写微信转盘小程序需求文档,这不比产品经理强?
前端
林的快手7 小时前
CSS列表属性
前端·javascript·css·ajax·firefox·html5·safari