瀑布流-轉載

jQuery瀑布流詳解(PC及移動端)

 

前言css

瀑布流佈局已成爲當今很是廣泛的圖片展現方式,不管是PC仍是手機等移動設備上。這種佈局圖片的樣式大概分爲三種:等高等寬、等寬不等高、等高不等寬,接下來咱們就最爲廣泛的等寬不等高形式來做爲示例。html

咱們用百度圖片做爲範例:算法

 

這就是PC端比較常見的瀑布流佈局方式,接下來咱們審查下元素看看百度圖片裏面是如何佈局:chrome

能夠看到,它裏面實際是若干個等寬的列容器,經過計算將圖片push到不一樣的容器裏。而本文介紹的展現方法是經過定位的方式,雖然最後佈局展現的方式不一樣,但以前的算法都比較相似。數組

 

動手瀏覽器

首先咱們將以下樣式的若干個單元寫進body中,並將「box」向左浮動:網絡

複製代碼
<div class="box">
    <img class="img" src="./resource/images/1.jpg" />
    <div class="desc">Description</div>
</div>
<div class="box">
    <img class="img" src="./resource/images/2.jpg" />
    <div class="desc">Description</div>
</div>
<div class="box">
    <img class="img" src="./resource/images/3.jpg" />
    <div class="desc">Description</div>
</div>
複製代碼

獲得以下效果:app

接下來:函數

複製代碼
var boxArr = $('.box'),
    num = Math.floor(document.body.clientWidth / boxArr.eq(0).outerWidth(true)),
     columnHeightArr = [];
columnHeightArr.length = num;
boxArr.each(function(index, item) {
    if (index < num) {
        columnHeightArr[index] = $(item).position().top + $(item).outerHeight(true);
    } else {
        var minHeight = Math.min.apply(null, columnHeightArr),
            minHeightIndex = $.inArray(minHeight,columnHeightArr);

        $(item).css({
            position: 'absolute',
            top: minHeight,
            left: boxArr.eq(minHeightIndex).position().left
        });
    }
});     
複製代碼

以上代碼大意爲:佈局

1. 首先計算出在瀏覽器中一行能容納多少圖片 (num) ,注意這裏用了outerWidth,當傳入true時會返回元素包括margin、padding、border所有盒模型屬性的尺寸;

2. 建立一個存儲每一列高度的數組 (columnHeightArr) ,該數組的長度即爲num值;

3. 遍歷全部圖片,將第一行的圖片高度分別存入列高數組中 (columnHeightArr) ,從第二行開始,首先計算出全部列中最小的高度 (minHeight) 以及最小高度所在的列 (minHeightIndex)。以後將第二行開始的圖片定位在高度最小列的下面,效果以下:

能夠看到雖然擺對了地方可是全部的圖片都放在同一個地方了,這是由於咱們須要在擺放一張圖片後就要增長該列的高度:

複製代碼
var boxArr = $('.box'),
    num = Math.floor(document.body.clientWidth / boxArr.eq(0).outerWidth(true)),
    columnHeightArr = [];
columnHeightArr.length = num;

boxArr.each(function(index, item) {
  if (index < num) {
    columnHeightArr[index] = $(item).position().top + $(item).outerHeight(true);
  } else {
    var minHeight = Math.min.apply(null, columnHeightArr),
           minHeightIndex = $.inArray(minHeight, columnHeightArr);

    $(item).css({
         position: 'absolute',
         top: minHeight,
         left: boxArr.eq(minHeightIndex).position().left
       });

    columnHeightArr[minHeightIndex] += $(item).outerHeight(true);
  }
});
複製代碼

 結果正確:

 

注意:上面的代碼須要運行於window.onload方法裏,由於只有當頁面中的圖片資源所有加載完畢後,其每張圖片的高度纔會有效。

所以會有一些很嚴重的問題,當網絡很差的時候圖片沒有徹底加載完成就會出現圖片展現不全高度缺失的狀況,這點在移動端很明顯。並且當咱們加載更多時,更難判斷新追加的圖片是否加載完成。

在實際生產中更不會有一開始就將圖片寫死在HTML中的狀況,因此咱們一般用如下的方式來作:

首先咱們在得到圖片地址時同時也須要獲取圖片的寬和高 ,這點對服務端後臺來講並非什麼難事,能夠拜託後臺兄弟將圖片的寬高數據拼進JSON,傳遞給你;

*接下來介紹小技巧,是一個朋友教個人,很是實用,它能保證一個元素不管大小如何變化,比例始終保持一致。這個技巧尤爲適用於移動端,由於元素爲了響應式一般使用百分比的形式。

假如手機頁面中有一張圖片,其寬度要爲屏幕的一半,高寬比爲2:1,須要在任何分辨的手機上保持比例不變。如何作?給元素設置以下屬性:

.box {
  width: 50%;
  height: 0;
  padding-bottom: 100%;
}

不設置高度,而是用padding「擠」出元素高度,而padding的百分比值都是基於父級容器的寬度。padding須要擠多少呢?就是寬度乘以高寬比(width和padding值均爲百分比值),這就是咱們爲何須要得到圖片尺寸的緣由。

效果:

能夠看到在chrome手機模擬器中ipone4和腎6Plus的顯示效果是徹底同樣的。在手機頁面中寬是固定的,而高會隨着頁面內容的多少而變化,這個技巧利用元素padding百分比的值實際上是基於其父級容器的寬,將高的值巧妙的轉化成與寬相關。

說到如今可能有人終於忍不住要問了,講了這麼多和瀑布流有什麼關係!簡單就是一句話,咱們要拋棄 img 標籤,而採用背景圖的方式。爲了使用背景圖,就得保持元素的比例永遠與圖片保持一致。

經過這種方式,能夠不用判斷圖片都加載完畢,直接產生一些與圖片同比例的div,再爲其設置背景圖,以下:

這裏好比最外層的box寬度爲220px,裏面的img元素寬度就能夠爲100%,高度就能夠經過padding擠出了。

 

懶加載

使用背景圖的方式還有好處那就是能夠比較方便的實現懶加載。那什麼是懶加載呢?就是當元素在咱們的視野中時才展現圖片,滾動時屏幕下方的圖片並不展現,這能夠很好的增長加載速度提高體驗。

首先咱們給最外層的box增長一個box-item類名(以後有用),將圖片url並不設置給backgroundImage屬性,而是賦給一個自定義屬性:data-src。

<div class="box box-item">
    <div class="img" data-src="./resource/images/1.jpg"></div>
    <div class="desc">Description</div>
</div>

接下來咱們編寫懶加載函數:

複製代碼
function lazyLoad() {
  var boxArr = $('.box-item');
  
  boxArr.each(function(index, item) {     var viewTop = $(item).offset().top - $(window).scrollTop(), imgObj = $(item).find('.img');
if ((viewTop < $(window).height()) && (($(item).offset().top + $(item).outerHeight(true)) > $(window).scrollTop())) {   imgObj.css('backgroundImage','url('+imgObj.attr("data-src")+')').removeClass('data-src');   $(item).removeClass('box-item'); } })
}
複製代碼

首先咱們獲取全部擁有 .box-item 類名的元素,遍歷。viewTop 爲圖片相對於瀏覽器窗口的相對高度,相似於position:fixed感受。

經過條件進行判斷,只有當該圖片在瀏覽器窗口內(之上或之下都不行)時,將須要設置背景圖元素的 data-src 值展現出來,並刪除該屬性。

以後將最外層元素的 box-item 刪除,由於已經展現出來的圖片不須要再進行這些判斷,刪除了該類名下一次滾動時就不會獲取到已經展現過的元素,須要遍歷的次數就會愈來愈少,這樣能起到一個優化的做用。

該函數須要在你的元素已經append進頁面時調用,以及在滾動時調用:

lazyLoad();
$(window).scroll(lazyLoad);

 

滾動加載

說完了懶加載,再說說滾動加載。所謂滾動加載就是當頁面滾動到底部附近時加載新的圖片。我這裏選擇的是滾動到高度最小的列底部時加載新的數據,你也能夠根據本身的喜愛來作判斷。

複製代碼
function scrollLoad() {
  var viewHeight = $(window).scrollTop() + $(window).height(),
        minHeight = Math.min.apply(null, columnHeightArr);

  if (viewHeight >= minHeight) {
     //loadMore...
  }
}
複製代碼

滾動加載也是在window的滾動事件中進行監聽,能夠與懶加載一塊兒進行:

$(window).scroll(function() {
    scrollLoad();
    lazyLoad();        
});

 

說完了PC端,咱們來講下手機端。其實原理是同樣的,只是從多列變成固定的兩列了。

複製代碼
var boxArr = $('.box'),
    columnHeightArr = [];
columnHeightArr.length = 2;

boxArr.each(function(index, item) {
  if (index < 2) {
    columnHeightArr[index] = $(item).position().top + $(item).outerHeight(true);
  } else {
    var minHeight = Math.min.apply(null, columnHeightArr),
           minHeightIndex = $.inArray(minHeight, columnHeightArr);

    $(item).css({
         position: 'absolute',
         top: minHeight,
         left: boxArr.eq(minHeightIndex).position().left
       });

    columnHeightArr[minHeightIndex] += $(item).outerHeight(true);
  }
});
複製代碼

不一樣的是爲了適應不一樣屏幕的手機,最外層的box容器寬度和邊距要設置成百分比的形式。

 

最後有一點要注意,由於咱們沒有像百度同樣用一個個列盒子去裝,而是用定位的方式。致使的問題是圖片元素的父級無法自適應高度,若是你有相關的需求咱們能夠計算出全部列中最長的長度,並將這個值賦值給父容器的min-height屬性:

$('body').css('minHeight',Math.max.apply(null, columnHeightArr));

 

整理下完整的代碼,瀑布流的全套服務就到這了 :)

複製代碼
    var dataArr = [
        {picUrl:'./resource/images/1.jpg',width:522,height:783},
        {picUrl:'./resource/images/2.jpg',width:550,height:786},
        {picUrl:'./resource/images/3.jpg',width:535,height:800},
        {picUrl:'./resource/images/4.jpg',width:578,height:504},
        {picUrl:'./resource/images/5.jpg',width:1440,height:900}
    ];

    $.each(dataArr, function(index, item) {
        $("body").append('<div class="box box-item">' +
                            '<div class="img" style="height:0;padding-bottom:'+cRate(item) * 100 + "%"+'" data-src="'+item.picUrl+'"></div>' +
                            '<div class="desc">Description</div>' +
                         '</div>');
    });

    var boxArr = $('.box'),
        num = Math.floor(document.body.clientWidth / boxArr.eq(0).outerWidth(true)),
        columnHeightArr = [];
    columnHeightArr.length = num;
    
    arrangement();  $('body').css('minHeight',Math.max.apply(null, columnHeightArr));

    lazyLoad();

    function arrangement() {
        boxArr.each(function(index, item) {
            if (index < num) {
                columnHeightArr[index] = $(item).position().top + $(item).outerHeight(true);
            } else {
                var minHeight = Math.min.apply(null, columnHeightArr),
                    minHeightIndex = $.inArray(minHeight, columnHeightArr);
                $(item).css({
                    position: 'absolute',
                    top: minHeight,
                    left: boxArr.eq(minHeightIndex).position().left
                });
                columnHeightArr[minHeightIndex] += $(item).outerHeight(true);
            }
        });
    }

    function lazyLoad() {
        var boxArr = $('.box-item');
        boxArr.each(function(index, item) {
            var viewTop = $(item).offset().top - $(window).scrollTop(),
                imgObj = $(item).find('.img');
            if ((viewTop < $(window).height()) && ($(item).offset().top + $(item).outerHeight(true) > $(window).scrollTop())) {
//                console.log($(item).attr('data-src'));
                imgObj.css('backgroundImage','url('+imgObj.attr("data-src")+')').removeClass('data-src');
                $(item).removeClass('box-item');
            }
        })
    }

    function cRate(obj) {
        return obj.height / obj.width;
    }

    function scrollLoad() {
        var viewHeight = $(window).scrollTop() + $(window).height(),
            minHeight = Math.min.apply(null, columnHeightArr);
        if (viewHeight >= minHeight) {
            //loadMore...
        }
    }

    $(window).scroll(function() {
        lazyLoad();
        scrollLoad();
    });以上內容轉載自:http://www.cnblogs.com/ghost-xyx/p/4916754.html
相關文章
相關標籤/搜索