效果:
-
瀑布流布局(Masonry Layout)是一种常见的网页布局方式,尤其适用于展示图片、卡片等内容。这种布局方式有以下特点:
-
不规则排列
:瀑布流布局允许元素高度不一,使得每个元素可以根据其内容自适应高度。 -
高效利用空间
:通过将新元素放置在当前高度最小的列中,瀑布流布局能够最大化地利用页面的垂直空间,减少空白区域,让页面看起来更加紧凑和美观。 -
动态加载
:瀑布流布局通常与无限滚动(Infinite Scroll)或懒加载(Lazy Loading)结合使用,以便在用户滚动页面时动态加载更多内容,提高用户体验和页面加载速度。 -
响应式设计
:瀑布流布局可以根据屏幕大小和设备类型进行调整,确保在不同设备上都有良好的显示效果。例如,可以根据屏幕宽度动态调整列数或元素大小。 -
视觉一致性
:尽管元素高度不一,但瀑布流布局通过精确的排列和对齐,使得页面整体看起来整齐有序,具有较好的视觉一致性。 -
流畅的用户体验
:使用瀑布流布局可以让用户在浏览内容时有一种连续流动的感觉,特别适合用于图片库、博客文章列表、商品展示等需要大量内容展示的场景。 -
复杂性管理
:实现瀑布流布局需要处理元素的绝对定位和动态计算,因此相对于简单的网格布局来说,技术实现稍显复杂,需要结合 JavaScript 来动态调整元素位置。
这些特点使得瀑布流布局成为一种非常灵活和实用的网页布局方式,特别适合展示大量内容并且希望保持页面整洁和美观的场景。 那怎样利用js实现上述的布局呢?
实现瀑布流布局
我们如何实现类似于花瓣网的瀑布流布局呢?我们先从花瓣网上下些图片,作为素材。
我们看下,如果我们只是利用简单的排列布局:
.item {
display: inline - block;
margin-bottom: 10px; /* 底部间距 */
border: 1px solid #ccc;
}
<div id="waterfall-container">
<div class="item">
<img src="./images/1.webp" alt="" />
</div>
<div class="item">
<img src="./images/2.webp" alt="" />
</div>
<div class="item">
<img src="./images/3.webp" alt="" />
</div>
<div class="item">
<img src="./images/4.webp" alt="" />
</div>
<div class="item">
<img src="./images/5.webp" alt="" />
</div>
<div class="item">
<img src="./images/6.webp" alt="" />
</div>
<div class="item">
<img src="./images/7.webp" alt="" />
</div>
<div class="item">
<img src="./images/8.webp" alt="" />
</div>
<div class="item">
<img src="./images/9.webp" alt="" />
</div>
<!-- 更多项目 -->
</div>
很显然这样不是很美观,而且页面空间没有得到进一步的利用。
那怎样实现瀑布流布局呢?
实现瀑布流布局的步骤如下:
1、获取父元素总宽度和图片宽度,确定第一行要展示的图片个数
n
:
计算父元素总宽度和图片宽度,然后根据父元素宽度和图片宽度计算出第一行要展示的图片个数
n
,这里存在不整除的问题,我们可以向下取整。重置父元素总宽度为
n * imgWidth
,然后设置父元素居中,并为图片元素设置padding: 5px
避免图片之间展示拥挤。使用一个长度为
n
的数组,来存储第一行各个图片的高度。2、从第
n + 1
张图片开始,找到高度数组中最小的元素索引index
,利用绝对定位放置图片,并同步更新该索引对应的高度:
遍历从第
n + 1
张图片开始的所有图片,找到高度数组中最小值对应的索引index
。将该图片绝对定位放置在父元素中,
top
值为高度数组中索引为index
的高度数据,left
值为index * 图片宽度
。更新高度数组中索引为
index
的值,加上该图片的高度。3、更新高度数组中索引为
index
的值,加上该图片的高度。
js代码实现
:
function calculateColumns() {
const container = document.getElementById('waterfall-container');
// Reset the width to auto to get the natural width of the container
container.style.width = 'auto';
const items = container.getElementsByClassName('item');
const parentWidth = container.clientWidth;
const itemWidth = items[0].offsetWidth + 10; // 10px for padding (5px on each side)
const n = Math.floor(parentWidth / itemWidth);
return { container, items, parentWidth, itemWidth, n };
}
function resetContainerWidth(container, n, itemWidth) {
container.style.width = n * itemWidth + 'px';
container.style.margin = '0 auto';
}
function positionItems(items, n, itemWidth) {
// Array to store heights of each column
const columnHeights = new Array(n).fill(0);
// Position each item
Array.from(items).forEach((item, i) => {
if (i < n) {
// First row items
item.style.top = '0px';
item.style.left = (i * itemWidth) + 'px';
columnHeights[i] = item.offsetHeight + 10; // Initial height with padding
} else {
// Find the shortest column
const minColIndex = columnHeights.indexOf(Math.min(...columnHeights));
item.style.top = columnHeights[minColIndex] + 'px';
item.style.left = (minColIndex * itemWidth) + 'px';
columnHeights[minColIndex] += item.offsetHeight + 10; // Update column height
}
});
}
function waterfallLayout() {
const { container, items, parentWidth, itemWidth, n } = calculateColumns();
resetContainerWidth(container, n, itemWidth);
positionItems(items, n, itemWidth);
}
// Initial layout
window.onload = waterfallLayout;
// Recalculate layout on window resize
window.onresize = waterfallLayout;
代码解读
:
-
函数
calculateColumns
:-
获取
waterfall-container
容器和其中的所有项目。 -
先将容器宽度重置为
auto
,以便页面尺寸变化后能够获取到其自然宽度。 -
获取父容器的宽度,并计算项目的宽度(包括 padding)。
-
计算每行能容纳的项目个数
n
。
-
-
函数
resetContainerWidth
:-
根据项目个数
n
和项目宽度itemWidth
重置父元素的宽度,使其居中。
-
-
函数
positionItems
:-
创建一个数组
columnHeights
来存储每列的高度。 -
遍历所有项目,第一行的项目直接放置在第一行,其余项目找到高度最小的列进行放置,并更新该列的高度。
-
-
函数
waterfallLayout
:-
调用
calculateColumns
获取必要的布局参数。 -
调用
resetContainerWidth
重置父元素的宽度。 -
调用
positionItems
重新布局所有项目。
-
-
事件监听:
-
在
window.onload
事件中调用waterfallLayout
初始化布局。 -
在
window.onresize
事件中调用waterfallLayout
,确保在窗口大小变化时重新计算布局。
-
通过上述步骤和代码,确保每次页面尺寸变化时都能正确获取最新的父容器宽度,并重新计算和调整瀑布流布局。
css代码
:
#waterfall-container {
position: relative;
margin: 0 auto;
}
#waterfall-container .item {
position: absolute;
padding: 5px;
box-sizing: border-box;
}
页面尺寸变化
: