瀑布流-转载

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
相关文章
相关标签/搜索