多种方式实现html瀑布流布局(flex,column-count,grid布局,float布局)

程序源码 阅读 49

瀑布流又称瀑布流式布局,以视觉表现为参差不齐的多栏布局为主,随着页面的滚动条不断向下滚动,这种布局会不断加载数据块并附加至当前尾部。实现这种布局有很多不同的方法:如flex布局,column-count 多栏布局,grid布局,float浮动布局。

flex实现瀑布流

先创建一个包住整个瀑布流的盒子,如果是2列,给该盒子设置display:flex;flex-wrap:wrap;完成这一步我们就可以有一个flex布局,超出该盒子宽度自动换行,然后在这个大盒子内部设置2个div容器,一列取奇数,一列取偶数即可简单进行实现

<view class="box">   
  <view  v-for="(item,idx) in  dataList"  :key="idx">
    <view v-if="idx%2==1">  {{item.img}} </view>
  </view>
  <view  v-for="(item,idx) in  dataList"  :key="idx">
    <view v-if="idx%2!=1"> {{item.img}} </view>
  </view>
</view>

column-count 多栏布局

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>column多行布局实现</title>
    <style>
      .box {
        margin: 10px;
        column-count: 3;  /* 是控制屏幕分为多少列 */
        column-gap: 10px; /* 控制列与列之间的距离 */
      }
      .item {
        margin-bottom: 10px;
      }
      img {
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="item">
        <img src="https://img2.baidu.com/it/u=1337068678,3064275007&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=750" alt="">
      </div>
      <div class="item">
        <img src="https://img2.baidu.com/it/u=2814429148,2262424695&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1422" alt="">
      </div>
      <div class="item">
        <img src="http://img2.baidu.com/it/u=3139110441,2189129638&fm=253&app=138&f=JPEG?w=800&h=1422" alt="">
      </div>
      <div class="item">
        <img src="http://img2.baidu.com/it/u=3444890671,796356424&fm=253&app=138&f=JPEG?w=800&h=1422" alt="">
      </div>
      <div class="item">
        <img src="http://img1.baidu.com/it/u=1286854971,3553024375&fm=253&app=138&f=JPEG?w=800&h=1422" alt="">
      </div>
      <div class="item">
        <img src="https://inews.gtimg.com/om_bt/O6SG7dHjdG0kWNyWz6WPo2_3v6A6eAC9ThTazwlKPO1qMAA/641" alt="">
      </div>
      <div class="item">
        <img src="https://inews.gtimg.com/om_bt/O0e2a37GGF5CDfNgK8GU29rF_2eJlHLDsa17LABXns7V4AA/641" alt="">
      </div>
      <div class="item">
        <img src="https://inews.gtimg.com/om_bt/Os3eJ8u3SgB3Kd-zrRRhgfR5hUvdwcVPKUTNO6O7sZfUwAA/641" alt="">
      </div>
      <div class="item">
        <img src="https://img0.baidu.com/it/u=1799694557,1475747482&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750" alt="">
      </div>
      <div class="item">
        <img src="https://pic.rmb.bdstatic.com/bjh/news/57e572cd41520408ebbbe5e3a6fb5b6d.jpeg" alt="">
      </div>
    </div>
 
    <script>
    </script>
  </body>
</html>

grid布局

先通过grid布局设置列数以及列的宽度,并通过js代码设置元素上边距和元素所需占行数

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>column多行布局实现瀑布流</title>
    <style>
      body {
        margin: 0;
      }
      .box {
        display: grid;
        grid-template-columns: repeat(1, 1fr); /* 声明列的宽度 fr实现等分响应式*/
        grid-gap: 0 20px; /* 声明行间距和列间距 */
        grid-auto-rows: 2px; /*  */
        align-items: end;
      }
      .item {
        display: flex;
        justify-content: center;
        align-items: center;
      }
      img {
        width: 100%;
        height: 100%;
      }
      /* 媒体查询 */
      @media (min-width: 1280px) and (max-width: 1920px) {
        .box {
          grid-template-columns: repeat(3, 1fr);
        }
      }
      @media (min-width: 768px) and (max-width: 1280px) {
        .box {
          grid-template-columns: repeat(2, 1fr);
        }
      }
      @media (max-width: 768px) {
        .box {
          grid-template-columns: repeat(1,1fr);
        }
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="item">
        <img src="https://img2.baidu.com/it/u=1337068678,3064275007&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=750" alt="">
      </div>
      <div class="item">
        <img src="https://img2.baidu.com/it/u=2814429148,2262424695&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1422" alt="">
      </div>
      <div class="item">
        <img src="http://img2.baidu.com/it/u=3139110441,2189129638&fm=253&app=138&f=JPEG?w=800&h=1422" alt="">
      </div>
      <div class="item">
        <img src="http://img2.baidu.com/it/u=3444890671,796356424&fm=253&app=138&f=JPEG?w=800&h=1422" alt="">
      </div>
      <div class="item">
        <img src="http://img1.baidu.com/it/u=1286854971,3553024375&fm=253&app=138&f=JPEG?w=800&h=1422" alt="">
      </div>
      <div class="item">
        <img src="https://inews.gtimg.com/om_bt/O6SG7dHjdG0kWNyWz6WPo2_3v6A6eAC9ThTazwlKPO1qMAA/641" alt="">
      </div>
      <div class="item">
        <img src="https://inews.gtimg.com/om_bt/O0e2a37GGF5CDfNgK8GU29rF_2eJlHLDsa17LABXns7V4AA/641" alt="">
      </div>
      <div class="item">
        <img src="https://inews.gtimg.com/om_bt/Os3eJ8u3SgB3Kd-zrRRhgfR5hUvdwcVPKUTNO6O7sZfUwAA/641" alt="">
      </div>
      <div class="item">
        <img src="https://img0.baidu.com/it/u=1799694557,1475747482&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750" alt="">
      </div>
      <div class="item">
        <img src="https://pic.rmb.bdstatic.com/bjh/news/57e572cd41520408ebbbe5e3a6fb5b6d.jpeg" alt="">
      </div>
    </div>
 
    <script>
      // 给每个元素模拟随机高度
      window.addEventListener('load', () => {
        document.querySelectorAll('.box > .item').forEach(item => {
          item.style.height = `${Math.floor(Math.random() * 200) + 100}px`
        })
      })
 
      const calcRows = () => {
        const box = document.querySelector('.box')
        const items = document.querySelectorAll('.item')
          // 获取当前列数
          const cols = getComputedStyle(box).gridTemplateColumns.split(" ").length
        
        items.forEach((item, index) => {
          // 给需要上下间隔的元素增加上间隔(每列第一个元素无需上间距)
          const gapRows = index >= cols ? 8 : 0;
          
          // 根据元素高度设置元素的需占行数
          const rows = Math.ceil(item.clientHeight / 2) + gapRows;
          // grid-row-end 属性:下边框所在的水平网格线
          item.style.gridRowEnd = `span ${rows}`;
        })
      }
 
      window.addEventListener('resize', calcRows)
      window.addEventListener('load', calcRows)
    </script>
  </body>
</html>

float布局

uni-app实现移动端瀑布流布局时需要先设置几列,在进行循环创建几个div,在进行浮动,然后在每个div里面放置图片,使用js代码计算每个图片高度

<template>
	<view class="container">
		<Header title="瀑布流" alignAway="center" class="sticky-pos"/>
		
		<!-- 浮动实现 -->
		<view class="waterfalls-flow">
			<view v-for="(item,index) in data.column" :key="index" class="waterfalls-flow-column"
				:style="{'width':w,'margin-left':index==0?0:m}" :id="`waterfalls_flow_column_${index+1}`">
				<view class="column-value" v-for="(item2,index2) in data[`column_${index+1}`]" :key="index2">
					<image :src="item2.image" mode="widthFix" @load="imgLoad(item2)" @error="imgError(item2)" class="imgsty">
					</image>
					<text>{{item2.title}}</text>
				</view>
			</view>
		</view>
	</view>
</template>
 
<script setup>
	import Header from '../components/header.vue';
	import {
		ref,
		reactive,
		watch,
		computed,
		getCurrentInstance,
		onMounted
	} from 'vue';
	const _this = getCurrentInstance();
	const data = reactive({
		list: [],
		column: 2,
		columnSpace: 1,
	});
	// 数据赋值
	data.list = [{
			image: 'https://via.placeholder.com/200x500.png/ff0000',
			title: '我是标题1',
			desc: '描述描述描述描述描述描述描述描述1'
		},
		{
			image: 'https://via.placeholder.com/200x200.png/2878ff',
			title: '我是标题2',
			desc: '描述描述描述描述描述描述描述描述2'
		},
		{
			image: 'https://via.placeholder.com/200x100.png/FFB6C1',
			title: '我是标题3',
			desc: '描述描述描述描述描述描述描述描述3'
		},
		{
			image: 'https://via.placeholder.com/200x300.png/9400D3',
			title: '我是标题4',
			desc: '描述描述描述描述描述描述描述描述4'
		},
		{
			image: 'https://via.placeholder.com/100x240.png/B0E0E6',
			title: '我是标题5',
			desc: '描述描述描述描述描述描述描述描述5'
		},
		{
			image: 'https://via.placeholder.com/140x280.png/7FFFAA',
			title: '我是标题6',
			desc: '描述描述描述描述描述描述描述描述6'
		},
		{
			image: 'https://via.placeholder.com/40x60.png/EEE8AA',
			title: '我是标题7',
			desc: '描述描述描述描述描述描述描述描述7'
		},
		{
			image: 'https://via.placeholder.com/200x500.png/ff0000',
			title: '我是标题1',
			desc: '描述描述描述描述描述描述描述描述1'
		},
		{
			image: 'https://via.placeholder.com/200x200.png/2878ff',
			title: '我是标题2',
			desc: '描述描述描述描述描述描述描述描述2'
		},
		{
			image: 'https://via.placeholder.com/200x100.png/FFB6C1',
			title: '我是标题3',
			desc: '描述描述描述描述描述描述描述描述3'
		},
		{
			image: 'https://via.placeholder.com/200x300.png/9400D3',
			title: '我是标题4',
			desc: '描述描述描述描述描述描述描述描述4'
		},
		{
			image: 'https://via.placeholder.com/100x240.png/B0E0E6',
			title: '我是标题5',
			desc: '描述描述描述描述描述描述描述描述5'
		},
		{
			image: 'https://via.placeholder.com/140x280.png/7FFFAA',
			title: '我是标题6',
			desc: '描述描述描述描述描述描述描述描述6'
		},
		{
			image: 'https://via.placeholder.com/40x60.png/EEE8AA',
			title: '我是标题7',
			desc: '描述描述描述描述描述描述描述描述7'
		},
	];
	// 计算列宽
	const w = computed(() => {
		const column_rate = `${100 / data.column - (+data.columnSpace)}%`;
		return column_rate;
	})
	// 计算margin
	const m = computed(() => {
		const column_margin =
			`${(100-(100 / data.column - (+data.columnSpace)).toFixed(5)*data.column)/(data.column-1)}%`;
		return column_margin;
	})
	// 每列的数据初始化
	for (let i = 1; i <= data.column; i++) {
		data[`column_${i}`] = [];
	}
	// 获取最小值的对象
	const getMin = (a, s) => {
		let m = a[0][s];
		let mo = a[0];
		for (var i = a.length - 1; i >= 0; i--) {
			if (a[i][s] < m) {
				m = a[i][s];
			}
		}
		mo = a.filter(i => i[s] == m);
		return mo[0];
	}
	// 计算每列的高度
	function getMinColumnHeight() {
		return new Promise(resolve => {
			const heightArr = [];
			for (let i = 1; i <= data.column; i++) {
				const query = uni.createSelectorQuery().in(_this);
				query.select(`#waterfalls_flow_column_${i}`).boundingClientRect(data => {
					heightArr.push({
						column: i,
						height: data.height
					});
				}).exec(() => {
					if (data.column <= heightArr.length) {
						resolve(getMin(heightArr, 'height'));
					}
				});
			}
		})
	};
	async function initValue(i) {
		if (i >= data.list.length) return false;
		const minHeightRes = await getMinColumnHeight();
		data[`column_${minHeightRes.column}`].push({
			...data.list[i],
			index: i
		});
	}
	onMounted(() => {
		initValue(0);
	})
	// 图片加载完成
	function imgLoad(item) {
		const i = item.index;
		initValue(i + 1);
	}
	// 图片加载失败
	function imgError(item) {
		const i = item.index;
		initValue(i + 1);
	}
</script>
 
<style scoped lang="scss">
	.container {
		background-color: rgb(237, 239, 246);
		height: 100%;
	}
	.sticky-pos {
		position: sticky;
		top: 0;
		z-index: 9;
		background-color: rgb(237, 239, 246);
		padding-bottom: 30rpx;
	}
	.waterfalls-flow {
		padding-top: 50rpx;
 
		&-column {
			float: left;
			padding: 0 0 200rpx;
		}
	}
 
	.column-value {
		width: 100%;
	}
 
	.imgsty {
		width: 100%
	}
</style>

相关推荐

用户留言

尚未登录无法发表查看留言点击登录

HI ! 请登录

立即登录
免费壁纸小程序
免费壁纸小程序