Skip to content

瀑布流实现的思路和做法

瀑布流(Waterfall Flow)是一种流行的网页布局方式,特点是等宽不等高,元素按照高度自动排列,形成类似瀑布的视觉效果。以下是实现瀑布流的几种常见方法:

实现思路

  1. 基本特点

    • 等宽不等高的元素
    • 元素自动填充到当前高度最小的列
    • 滚动加载更多内容
  2. 核心算法

    • 计算每列当前高度
    • 将新元素插入到高度最小的列
    • 更新该列的高度

实现方法

1. 纯CSS实现(CSS Grid或Columns)

css
/* 多列方式 */
.container {
    column-count: 4; /* 列数 */
    column-gap: 15px;
}

.item {
    break-inside: avoid; /* 防止元素被分割到不同列 */
    margin-bottom: 15px;
}

/* 或使用CSS Grid */
.container {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-gap: 15px;
    grid-auto-flow: dense;
    /**
    `grid-auto-flow` 属性控制自动放置的算法,可选值包括:
  - `row`:默认值,按行依次填充。
  - `column`:按列依次填充。
  - `dense`:密集模式,尽可能填满网格的空隙。
    */
}

优点:简单,无需JavaScript
缺点:元素按列顺序排列,不是严格的高度优先

2. JavaScript实现(计算位置)

javascript
function waterfall(container, itemClass, columnCount) {
    const containerEl = document.querySelector(container);
    const items = document.querySelectorAll(itemClass);
    const gap = 15; // 间距

    // 初始化列高度数组
    const colHeights = new Array(columnCount).fill(0);
    const containerWidth = containerEl.offsetWidth;
    const itemWidth = (containerWidth - (columnCount - 1) * gap) / columnCount;

    items.forEach((item) => {
        // 设置元素宽度
        item.style.width = `${itemWidth}px`;

        // 找到高度最小的列
        const minHeight = Math.min(...colHeights);
        const minIndex = colHeights.indexOf(minHeight);

        // 设置元素位置
        item.style.position = 'absolute';
        item.style.left = `${minIndex * (itemWidth + gap)}px`;
        item.style.top = `${minHeight}px`;

        // 更新列高度
        colHeights[minIndex] += item.offsetHeight + gap;
    });

    // 设置容器高度
    containerEl.style.height = `${Math.max(...colHeights)}px`;
}

// 使用示例
window.addEventListener('load', () => waterfall('.container', '.item', 4));
window.addEventListener('resize', () => waterfall('.container', '.item', 4));

3. 使用现成库

javascript
// 使用Masonry示例
var msnry = new Masonry('.grid', {
    itemSelector: '.grid-item',
    columnWidth: 200,
    gutter: 10
});

4. 响应式瀑布流

javascript
function responsiveWaterfall() {
    const container = document.querySelector('.container');
    const screenWidth = window.innerWidth;
    let columns = 4;

    if (screenWidth < 768) columns = 2;
    else if (screenWidth < 1024) columns = 3;

    waterfall('.container', '.item', columns);
}

window.addEventListener('resize', responsiveWaterfall);

优化考虑

  1. 图片懒加载:对于图片较多的瀑布流,实现懒加载
    html
    <img data-src="real-image.jpg" src="placeholder.jpg" class="lazyload" />
javascript
// 优化后的代码
const lazyImages = document.querySelectorAll('.lazyload');

// 创建单个IntersectionObserver实例(减少内存占用)
const observer = new IntersectionObserver(
    (entries) => {
        entries.forEach((entry) => {
            // 使用requestAnimationFrame优化滚动性能
            if (entry.isIntersecting) {
                requestAnimationFrame(() => {
                    const img = entry.target;
                    // 添加错误处理和备用方案
                    img.onerror = () => {
                        console.warn('Lazy load failed:', img.dataset.src);
                        img.classList.add('lazyload-error');
                        observer.unobserve(img);
                    };

                    // 使用srcset支持响应式图片
                    if (img.dataset.srcset) img.srcset = img.dataset.srcset;
                    if (img.dataset.src) img.src = img.dataset.src;

                    // 添加加载状态标记
                    img.classList.add('lazyloaded');
                    img.classList.remove('lazyload');

                    // 停止观察已加载图片
                    observer.unobserve(img);
                });
            }
        });
    },
    {
        // 添加配置选项
        rootMargin: '100px 0px', // 提前100px加载
        threshold: 0.01 // 至少1%可见
    }
);

// 批量观察元素(减少函数调用)
lazyImages.forEach((img) => {
    // 预加载占位符检查
    if (!img.dataset.src || img.complete) return;
    observer.observe(img);
});
  1. 滚动加载更多

    javascript
    window.addEventListener('scroll', () => {
        if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 500) {
            // 加载更多内容
        }
    });
  2. 性能优化

    • 使用防抖(debounce)处理resize事件
    • 使用虚拟滚动(Virtual Scrolling)处理大量元素

现代CSS方案

使用CSS Grid的grid-auto-flow: dense可以实现类似效果:

css
.container {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    grid-auto-flow: dense;
    grid-gap: 15px;
}

.item {
    /* 不同高度由内容决定 */
}

/* 可以设置不同项目的跨度 */
.item.tall {
    grid-row: span 2;
}

选择哪种实现方式取决于项目需求、浏览器兼容性要求和性能考虑。