手摸手,帶你實現移動端H5瀑布流布局

移動端瀑布流布局是一種比較流行的網頁布局方式,視覺上來看就是一種像瀑布一樣垂直落下的排版。每張圖片並不是顯示的正正方方的,而是有的長有的短,呈現出一種不規則的形狀。但是它們的寬度通常都是相同的

因為移動端瀑布流布局主要為豎向瀑布流,因此本文所探討的是豎向瀑布流

特點

豎向瀑布流布局主要有下面幾種特點:

  • 一般出現在移動端 H5 頁面底部
  • 主要以圖片或影片為主
  • 降低頁面複雜度,節省空間,可以容納更多內容
  • 不規則展示,不會那麼枯燥,用戶體驗好
  • 難以或者說不能滾動到頁面最底部

不同於傳統的分頁,瀑布流因為以上這些特點一般被用在這些場景下:

  • 推薦機制下的資訊 即根據用戶畫像推薦或者運營人員推薦的資訊
  • 大分類下的資訊流 展示的資訊有很多,它們的大分類都是相同的,適合用戶不明確詳細需要獲得什麼資訊或商品的情況下
  • 各個資訊或商品之間沒有比較強的相關性 和上面一條類似,展示的不是千遍一律的東西,相對獨立的資訊或商品也許能讓用戶意外發掘到想要的東西

實現

一般來說主要分為 CSS 實現和 JS 實現

CSS 實現主要是用到一些專門的樣式屬性,實現起來簡單,但是往往會有兼容性問題

JS 實現的方法則不存在這些問題,並且能實現比較個性化的需求,但是實現起來比較麻煩

column 多列布局方法

column 實現瀑布流主要依賴兩個屬性

column-count 屬性是設置共有幾列

column-gap 屬性是設置每列之間的間隔

column 兼容性

程式碼

<style>
  .pic {
    column-count: 3;
    column-gap: 5px;
  }

  .pic .item {
    border: 1px solid #ccc;
    margin-bottom: 5px;
  }

  .item img {
    width: 100%;
  }
</style>

<body>
  <div class="pic">
    <div class="item">
      <!-- 獲取 api 取到的圖片地址 -->
      <img src="" />
      <div>001</div>
    </div>
    ······
    <div class="item">
      <img src="" />
      <div>008</div>
    </div>
  </div>
</body>

flex 彈性布局方法

flex 實現瀑布流需要給父元素設置為橫向排列。然後通過設置 flex-flow: column wrap 使其換⾏

程式碼

<style>
  .pic {
    display: flex;
    flex-flow: column wrap;
    height: 100vh;
  }

  .item {
    /* 每行展示 3 個 */
    width: calc(100%/3 - 5px);
    border: 1px solid #ccc;
    margin-bottom: 5px;
  }

  .item img {
    width: 100%;
  }
</style>
<body>
  <div class="pic">
    <div class="item">
      <!-- 獲取 api 取到的圖片地址 -->
      <img src="" />
      <div>001</div>
    </div>
    ······
    <div class="item">
      <img src="" />
      <div>008</div>
    </div>
  </div>
</body>

效果

可以發現圖片排序順序是先垂直方向,然後才是水平方向的。column 多列布局和 flex 彈性布局方法實現的效果圖最終相似

JS + 懶載入方法

在不考慮兼容性或者沒有特殊圖片展示順序需求下,只是實現瀑布流的話上面兩種方案是夠用的。如果要實現一些個性化的需求的話,還是得用 JS

主要思路就是:

  1. 先將第一行排滿
  2. 計算第一行的所有圖片高度,將第二行第一張圖放在第一行最矮的圖片後面
  3. 進行玩步驟 2,重新計算當前所有列高度,避免步驟 2 添加完成後,該列高度還是最矮

完整程式碼

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>JS實現瀑布流</title>
    <style>
        .item {
            box-sizing: border-box;
            border: 1px solid #ccc;
            position: absolute;
            /* 展示為三列,減去間隔寬度 */
            width: calc(100% / 3 - 5px);
        }

        .item img {
            width: 100%;
        }
    </style>
</head>

<body>
    <div id="pic">
        <div class="item">
            <img src="..." />
            <div>001</div>
        </div>
        <!-- 剩餘圖片,實際場景中應該使用for循環 -->
        <div class="item">
            <img src="..." />
            <div>015</div>
        </div>
    </div>
</body>
<script>
    var pic = document.getElementById('pic');
    var items = pic.children;
    // 相鄰間距為5像素
    var space = 5;
    var picAmount = 15;

    window.onload = function () {
        function getClient() {
            return {
                width: window.innerWidth ||
                    document.documentElement.clientWidth ||
                    document.body.clientWidth,
                height: window.innerHeight ||
                    document.documentElement.clientHeight ||
                    document.body.clientHeight,
            };
        }

        function getScrollTop() {
            return document.documentElement.scrollTop ||
                window.pageYOffset ||
                document.body.scrollTop;
        }

        waterFall();

        function waterFall() {
            var pageWidth = getClient().width;
            var itemWidth = items[0].offsetWidth;
            var columns = parseInt(pageWidth / (itemWidth + space));
            var picList = [];
            for (var i = 0; i < items.length; i++) {
                if (i < columns) {
                    items[i].style.top = 0;
                    items[i].style.left = (itemWidth + space) * i + 'px';
                    picList.push(items[i].offsetHeight);
                } else {
                    // 找到數組中最小高度的那一列,並拿到其下標
                    var minHeight = picList[0];
                    var index = 0;
                    for (var j = 0; j < picList.length; j++) {
                        if (minHeight > picList[j]) {
                            minHeight = picList[j];
                            index = j;
                        }
                    }
                    items[i].style.top = picList[index] + space + 'px';
                    items[i].style.left = items[index].offsetLeft + 'px';
                    // 操作列的高度 = 當前列原本高度 + 圖片的高度 + 相鄰的間距
                    picList[index] = picList[index] + items[i].offsetHeight + space;
                }
            }
        }
        window.onresize = function () {
            waterFall();
        };

        // 監聽滾動事件,模擬懶載入
        window.onscroll = function () {
            // 如果滾動到的位置比當前顯示的最後一個圖片高,則請求新的圖片
            if (
                getClient().height + getScrollTop() >=
                items[items.length - 1].offsetTop
            ) {
                // 後續的新圖片
                var datas = [...];
                for (var i = 0; i < datas.length; i++) {
                    picAmount += 1;
                    var div = document.createElement('div');
                    div.className = 'item';
                    div.innerHTML = `<img src="${datas[i]}" alt=""><div>0${picAmount}</div>`;
                    pic.appendChild(div);
                }
                waterFall();
            }
        };
    };
</script>

</html>

效果

不同於上面兩個 css 實現的瀑布流,JS 實現的圖片排序順序是先水平方向,然後才是垂直方向

可以看到當滾動頁面的時候,新的圖片會不斷添加進來,這樣就實現懶載入了

總結

如果實現效果簡單不考慮兼容的的話可以選擇使用 CSS 實現;若要兼容老版本瀏覽器或者實現一些個性化的需求還是得用 JS 實現

當然除了上文說的這些方法以外,也可以使用第三方庫 Masonry 實現

Tags: