瀑布流布局原理

基本思路

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

效果如图:

相关推荐
C语言魔术师17 分钟前
【小游戏篇】三子棋游戏
前端·算法·游戏
匹马夕阳1 小时前
Vue 3中导航守卫(Navigation Guard)结合Axios实现token认证机制
前端·javascript·vue.js
你熬夜了吗?1 小时前
日历热力图,月度数据可视化图表(日活跃图、格子图)vue组件
前端·vue.js·信息可视化
桂月二二8 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
hunter2062069 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb9 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角9 小时前
CSS 颜色
前端·css
九酒9 小时前
从UI稿到代码优化,看Trae AI 编辑器如何帮助开发者提效
前端·trae
浪浪山小白兔10 小时前
HTML5 新表单属性详解
前端·html·html5
lee57610 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm