文章目录
  1. 1. 背景
  2. 2. 静态瀑布流实现过程
    1. 2.1. 准备工作
    2. 2.2. 核心过程
  3. 3. 动态瀑布流实现过程
  4. 4. 结尾

背景

  积累久了的知识还是要拿出来顺一顺,碰巧这几天研究瀑布流,就顺带理清了瀑布流的实现思路,也用ajax实现了下拉从数据库加载数据的效果,并且对实现过程中遇到的问题作出了解决。代码也已经放上了github,有需要者自行下载。

  瀑布流相信大家都不会陌生,据说最早是Pinterest网站使用的网页布局,后来流行于各大网站。
  瀑布流的实现有几种方式,详情可见:淘宝网UED官方博客
  而我所用的方法是使用JavaScript来实现,其他方法在这里就不多描述了。

静态瀑布流实现过程

下载地址

准备工作

  1. html的基本页面布局为

    1
    2
    3
    <div id="container" class="container">
    <div class="imgShow"><img src=""/></div>
    </div>
  2. css的设置为

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    .container {
    position: absolute;
    top: 20px;
    width:100%;
    }
    .imgShow {
    position: absolute;
    border: solid 1px #ccc;
    padding: 10px;
    width: 200px;
    top: 0px;
    left: 0px;
    }
    img { width: 100%; }

核心过程

实现瀑布流的过程中使用了函数的prototype属性,当创建一个函数实例的时候,实例可以共享原型对象包含的属性和方法。

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,
而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
使用原型对象的好处是可以让所有的对象实例共享它包含的属性和方法。
–《JavaScript高级程序设计》

  1. 首先定义需要的属性。
    可以把瀑布流拆成三个部分来看:容器、列、格子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     function WaterFall(containerId){
    this.container = document.getElementById(containerId);
    this.conWidth = 0;//container容器宽度
    this.boxWidth = 0;//每一个格子的宽度
    this.columnNum = 1; //分为多少列
    this.columnHeight = [];//存储每列的高度
    this.boxTagName = "div";
    this.boxList = []; //所有的格子的对象
    this.init();
    }
  2. 初始化数据,并且计算出每行可以放下的格子的数量n = 屏幕可见区域宽度/(格子宽度+间距)。

    1
    2
    3
    4
    5
    6
    7
    8
    WaterFall.prototype.init = function(){
    this.boxList = this.container.getElementsByTagName(this.boxTagName);
    this.boxWidth = this.boxList[0].offsetWidth + 10;
    this.conWidth = this.container.offsetWidth;
    // 计算每行可以放下的格子数量
    var n = parseInt(this.conWidth/this.boxWidth);
    this.columnNum = (n > 0) ? n : 1;
    }
  3. 进行排序:在上一步中得到每行可以放下的格子的数量n后,把前n个格子放在第一行每一列中;然后每次寻找高度最小的一列,依次把格子放进去,并通过设置left(容器离左边的宽度+间距+格子的宽度*最低列索引)和top(间距+最低列高度)来实现定位,最后刷新新列的高度,遍历所有格子直到格子全都被排序。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    WaterFall.prototype.show = function(){
    var len = this.boxList.length;
    var conLeft = this.container.offsetLeft;
    var conTop = this.container.offsetTop;
    var marginTop = 10;
    var marginLeft = 10;
    for(var i=0; i<len; i++){
    if(i<this.columnNum){
    this.boxList[i].style.left = conLeft + marginLeft + i*this.boxWidth + 'px';
    this.boxList[i].style.top = conTop + 'px';
    this.columnHeight[i] = conTop + this.boxList[i].offsetHeight;
    }else{
    var minColum = this.getMinHeightCol();
    var boxHeight = this.boxList[i].offsetHeight + marginTop;
    this.boxList[i].style.left = conLeft + marginLeft + minColum*(this.boxWidth ) + 'px';
    this.boxList[i].style.top = this.columnHeight[minColum] + marginTop + 'px';
    this.columnHeight[minColum] += boxHeight;
    }
    }
    }
  4. 加载函数,实例化对象。

    1
    2
    3
    4
    window.onload = function(){
    var water = new WaterFull("container");
    water.show();
    }
  5. 当浏览器窗口被缩放时,进行页面重排渲染。

    1
    2
    3
    4
    window.onresize = function(){
    var water = new WaterFull("container");
    water.show();
    }

  考虑到当窗口大小在进行改变但未终止时都要对瀑布流进行重排渲染的话,网页性能会大大降低,所以我采用setTimeout的方法来适当延时窗口大小改变后瀑布流的重排渲染。上面的代码改为:

1
2
3
4
5
6
7
8
 var timer;
window.onresize = function(){
clearTimeout(timer);
timer = setTimeout(function(){
var water = new WaterFall("container");
water.show()
},300);
}

进行到这里,静态的瀑布流效果就完成了。但是现在很多网页不会一下子把格子里面的内容从数据库中导出,而是通过ajax来实现分页请求数据。

动态瀑布流实现过程

下载地址

  1. html部分在上面的基础上加上

    1
    <input type="hidden" name="currentPage" id="currentPage" value="1">
  2. 核心部分跟静态的一样,通过ajax请求数据,每次请求15条数据(其实实现下拉ajax分页请求数据与后台php分页类似)。前提是自己用php写了一个上传图片的功能,并将图片暂存到了uoloads文件夹中,详情请看github

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function getImage(){
    var html = "";
    var currentPage = parseInt($("#currentPage").val());
    $.ajax({
    url:"getAjax.php",
    type:'post',
    data:"currentPage="+currentPage,
    dataType:'json',
    success:function(result){
    var data = result;
    $.each(data,function(index,value){
    html = " <div class='imgShow'><img src='uploads/"+ value.photo+"'/> </div> ";
    $(".container").append(html);
    var water = new WaterFall("container");
    water.show();
    });
    $("#currentPage").val(currentPage+1);
    }
    })
    }
  3. 下拉加载判断:当滚动条的高度+窗口可视高度==整个文档的高度时,再请求多一页数据,这里的判断条件比较随意,有兴趣者可以进行改进。

    1
    2
    3
    4
    5
    $(window).scroll(function(event){
    if($(window).scrollTop() + $(window).height() == $(document).height()){
    getImage();
    }
    });

  测试了一下,咦?为什么有些图片重叠了,但是在下拉的过程中又正常排列了,一开始我以为是函数的加载顺序搞错了,调试了之后是没问题的,后来看到一种现象,如下图:
waterfall

  • 有问题的代码测试为waterfallAjax2
      经过分析,觉悟到是前一张图片还没有加载完,后一张图片就贴上来的,所以会出现重叠的现象。于是我进行了图片预加载工作。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function preLoad(src){
    var img = new Image();
    img.src = src;
    var html= "";
    img.onload = function(){
    html = " <div class='imgShow'><img src='"+ src+"'/> </div> ";
    $(".container").append(html);
    // 瀑布流显示图片
    var water = new WaterFall("container");
    water.show();
    }
    }

经过这一步后,瀑布流正常显示,瞬间感觉自己棒棒哒~

结尾

  经过整个项目的实现,自己对ajax以及页面布局又有了更好的了解,并且学会在项目中运用自己所学的知识,以达到巩固的效果。

文章目录
  1. 1. 背景
  2. 2. 静态瀑布流实现过程
    1. 2.1. 准备工作
    2. 2.2. 核心过程
  3. 3. 动态瀑布流实现过程
  4. 4. 结尾