瀑布流布局原理

基本思路

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

效果如图:

相关推荐
bin91533 分钟前
前端JavaScript导出excel,并用excel分析数据,使用SheetJS导出excel
前端·javascript·excel
Rattenking11 分钟前
node - npm常用命令和package.json说明
前端·npm·json
Easonmax11 分钟前
【HTML5】html5开篇基础(1)
前端·html·html5
For. tomorrow15 分钟前
Vue3中el-table组件实现分页,多选以及回显
前端·vue.js·elementui
布瑞泽的童话42 分钟前
无需切换平台?TuneFree如何搜罗所有你爱的音乐
前端·vue.js·后端·开源
白鹭凡1 小时前
react 甘特图之旅
前端·react.js·甘特图
打野赵怀真1 小时前
你有看过vue的nextTick源码吗?
前端·javascript
2401_862886781 小时前
蓝禾,汤臣倍健,三七互娱,得物,顺丰,快手,游卡,oppo,康冠科技,途游游戏,埃科光电25秋招内推
前端·c++·python·算法·游戏
书中自有妍如玉1 小时前
layui时间选择器选择周 日月季度年
前端·javascript·layui