原生js實現瀑布流效果

image

前言

最近在整理js基礎知識,接觸到了幾個經常使用的頁面特效,其中以爲用原生js實現瀑布流的案例十分有趣,因而與你們分享一下。html

瀑布流

瀑布流,又稱瀑布流式佈局。是比較流行的一種網站頁面佈局,視覺表現爲良莠不齊的多欄佈局,隨着頁面滾動條向下滾動,這種佈局還會不斷加載數據塊並附加至當前尾部。

image

一、首先瀑布流全部的圖片應該保持寬度一致,高度是由內容決定。ajax

image

左浮動的話,咱們能夠看到第6個盒子直接就在第4個盒子旁邊停下了,由於第4個高度最高,擋住了它左浮動的去路。第6個盒子是第2行的最後一個,因此第7個盒子只能在第3行排列了。當排到第12個盒子的時候,盒子會以第11個盒子的位置爲基礎左浮動(這就是第12個盒子爲何沒有‘跳到’第9個盒子下面的緣由),碰到第8個盒子後又被擋住了。數組

image

經過定位的方式是咱們實現瀑布流的最基本的原理,只要咱們動態的設置它的top值、left值,就能讓它排列。瀏覽器

二、定位後肯定瀏覽器顯示區域內,一行能放多少列圖片盒子。app

  • 獲取頁面的寬度
  • 獲取圖片盒子的寬度
  • 顯示的列數 = 頁面寬度/圖片盒子寬度
  • column = pageWidth / itemWidth

image

三、爲了美觀咱們能夠加上一個空隙函數

  • 顯示的列數 = 頁面寬度/(圖片盒子寬度+間隙);
  • column = pageWidth / (itemWidth + gap);

image

四、 肯定列數以後,排列第一行佈局

  • 下面還有不少圖片盒子,咱們先要排列第1行,因此在for循環裏就要判斷一下,當i(全部圖片盒子的索引) < column(顯示列數)的時候,說明在第1行;
  • 知道在第1行以後,動態設置每一個圖片盒子的left值就能排好第1行。
  • left = i * ( itemWidth + gap );

image

五、第1行排列好以後,獲取第1行全部圖片盒子的高度網站

  • 須要定義一個數組arr,將獲取到的高度存在數組中,由於第2行排列的時候須要考慮top值,此時只能根據第1行圖片盒子的高度來設置;
  • 獲取圖片高度的時候要注意,程序必須寫在入口函數onload裏面,由於圖片的加載特性是:等頁面都加載完以後纔去請求加載,因此不寫在入口函數裏可能會出現高度獲取不到的狀況。

image

六、排列第2行spa

  • 獲取到剛剛數組中,高度最小的那一列,將第2行的第1個圖片盒子放置在它的下方;
  • 此時的left值就是高度最小列的offsetLefttop值就是:第1行高度最小列的高度(爲了佈局美觀能夠加上上下間隙gap)。
  • 記錄下高度最小列的索引index,後面計算會用到;
  • 設置完成以後,會發現後面全部的圖片都疊在這個高度最小列的下面,緣由就是此時的最小列高度沒有改變,應該加上下面圖片的高度,得出一個新高度。

image

七、改變最小列當前高度code

  • 此時的這一列高度其實已經發生改變了,因此須要將新高度賦值給數組
  • 當前高度最小列的高度 = 當前高度最小列的高度 + 間隙 + 下面圖片盒子的高度

image

八、觸發resize事件

  • 將整個設置樣式的部分封裝成一個函數,在onload裏面註冊一個resize事件,只要頁面一發生改變,就觸發樣式部分的代碼。
  • 實時改變pageWidth的寬度,這樣瀑布流就會是一個響應式的效果了

九、懶加載效果

  • 目前咱們用的是30張圖片,假如一個頁面中有幾百張圖片的時候,咱們不可能等到它都加載完再顯示,全部這裏引入一個懶加載的概念,咱們規定第30張爲顯示的最後一張圖片,當滾動條滾動到30張的時候,應該加載下一批圖片。
  • 即頁面可視區高度+滾動條捲去的高度 = 第30圖片的offsetTop;的時候加載下面的圖片。

image

完整代碼:

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
        position: relative;
    }
    
    img {
        width: 220px;
        display: block;
    }
    
    .item {
        box-shadow: 2px 2px 2px #999;
        position: absolute;
    }
</style>

<!-- html 部分 -->
<div id="box">
    <div class="item"><img src="../image/瀑布流/001.jpg" alt=""></div>
                                .
                                .
                                .
    <div class="item"><img src="../image/瀑布流/030.jpg" alt=""></div>
</div>

<!-- js 部分 -->
<script>
    var box = document.getElementById('box');
    var items = box.children;
    // 定義每一列之間的間隙 爲10像素
    var gap = 10;

    window.onload = function() {
        // 一進來就調用一次
        waterFall();
        // 封裝成一個函數
        function waterFall() {
            // 1- 肯定列數  = 頁面的寬度 / 圖片的寬度
            var pageWidth = getClient().width;
            var itemWidth = items[0].offsetWidth;
            var columns = parseInt(pageWidth / (itemWidth + gap));
            var arr = [];
            for (var i = 0; i < items.length; i++) {
                if (i < columns) {
                    // 2- 肯定第一行
                    items[i].style.top = 0;
                    items[i].style.left = (itemWidth + gap) * i + 'px';
                    arr.push(items[i].offsetHeight);

                } else {
                    // 其餘行
                    // 3- 找到數組中最小高度  和 它的索引
                    var minHeight = arr[0];
                    var index = 0;
                    for (var j = 0; j < arr.length; j++) {
                        if (minHeight > arr[j]) {
                            minHeight = arr[j];
                            index = j;
                        }
                    }
                    // 4- 設置下一行的第一個盒子位置
                    // top值就是最小列的高度 + gap
                    items[i].style.top = arr[index] + gap + 'px';
                    // left值就是最小列距離左邊的距離
                    items[i].style.left = items[index].offsetLeft + 'px';

                    // 5- 修改最小列的高度 
                    // 最小列的高度 = 當前本身的高度 + 拼接過來的高度 + 間隙的高度
                    arr[index] = arr[index] + items[i].offsetHeight + gap;
                }
            }
        }
        // 頁面尺寸改變時實時觸發
        window.onresize = function() {
            waterFall();
        };
        // 當加載到第30張的時候
        window.onscroll = function() {
            if (getClient().height + getScrollTop() >= items[items.length - 1].offsetTop) {
                // 模擬 ajax 獲取數據    
                var datas = [
                    "../image/瀑布流/001.jpg",
                            ...
                    "../image/瀑布流/030.jpg"
                ];
                for (var i = 0; i < datas.length; i++) {
                    var div = document.createElement("div");
                    div.className = "item";
                    div.innerHTML = '<img src="' + datas[i] + '" alt="">';
                    box.appendChild(div);
                }
                waterFall();
            }

        };
    };

    // clientWidth 處理兼容性
    function getClient() {
        return {
            width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
            height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
        }
    }
    // scrollTop兼容性處理
    function getScrollTop() {
        return window.pageYOffset || document.documentElement.scrollTop;
    }
</script>

效果圖:

image

相關文章
相關標籤/搜索