详解javascript实现瀑布流绝对式布局


Posted in Javascript onJanuary 29, 2016

瀑布流也应该算是流行几年了吧。首先是由Pinterest掀起的浪潮,然后国内设计如雨后春笋般,冒出很多瀑布流的例子,比如,蘑菇街,Mark之(不过最近涉黄,好像被喝茶了),还有淘宝的 “哇哦”. 这些都是很棒的例子, 今天我们就聊一聊瀑布流。
一、绝对式布局:

详解javascript实现瀑布流绝对式布局

JS实现原理

其实瀑布式主要的难点就在于,如果将图片整齐的排列在对应的列下,以及什么时候开始刷新加载图片。
而图片整齐的排列的主要逻辑和算法即,先获取容器内可以放多少列,然后,通过计算,存放第一列的高度,再遍历剩下(除第一列的元素)的高度,分别放入,高度最小的那一列。 逐个添加,最后结束遍历。
开始刷新的设置就很简单了,瀑布流刷新只和一个事件有关,即,window.onscroll. 主要的算法即,当页面滑动到最低高度的时候开始加载节点并且添加,当然,节点添加的个数是不固定的。
先上代码吧,我这里分3部分讲解,一个是图片的排列,一个是设置加载的位置.另外再补充一个响应式加载。

1、图片排列

var $ = function() {
  return document.querySelectorAll.apply(document, arguments);
 }
 var arrHeight = []; //得到分列的高度
 var columns = function() { //计算页面最多可以放多少列
  var containerW = $("#main")[0].clientWidth,
   pinW = $(".pin")[0].offsetWidth;
  return Math.floor(containerW / pinW);
 }
 var getIndex = function(arr) { //获得最小高度的index
   var minHeight = Math.min.apply(null, arr); //获得最小高度
   for (var i in arr) {
    if (arr[i] === minHeight) {
     return i;
    }
   }
  }
  //根据列数确定第一排img的高度并放入数组当中。
 var setCenter = (function() { //通过列数设置宽度
  var main = $('#main')[0]; //获得罩层
  var getPadding = function() { //设置padding
   var col = columns(); //获得最后一列
   var padding = main.clientWidth - col * $('.pin')[0].offsetWidth;
   return padding / 2;
  }
  var getComputedStyle = function(ele) { //兼容IE的支持情况
   if (window.getComputedStyle) {
    return window.getComputedStyle(ele);
   } else {
    return ele.currentStyle;
   }
  }
  var getPinPad = function() { //获得pin的padding值
   var pin = $(".pin")[0];
   return parseInt(getComputedStyle(pin).paddingLeft);
  }
  return function() { //设置宽度
   main.style.padding = `0 ${getPadding()}px 0 ${getPadding()-getPinPad()}px`;
  }
 })();
 var overLoad = function(ele) {
  var index = getIndex(arrHeight),
   minHeight = Math.min.apply(null, arrHeight), //获取最小高度
   pins = $('.pin'),
   style = ele.style;
  style.position = "absolute";
  style.top = minHeight + "px"; //设置当前元素的高度
  style.left = pins[index].offsetLeft + "px";
  arrHeight[index] += ele.offsetHeight;
 }
 //初始化时执行函数
 var init = function() {
   var pins = $(".pin"),
    col = columns();
   setCenter(); //设置包裹容器的宽度
   for (var i = 0, pin; pin = pins[i]; i++) {
    if (i < col) { //存储第一排的高度
     arrHeight.push(pin.offsetHeight);
    } else {
     overLoad(pin); //将元素的位置重排
    }
   }
  }

里面一共有7个函数(大函数),一个变量。 说一下思路吧。 首先,页面onload之后 执行的函数是 init. 要知道,一个js程序一定有他的入口,关键看你怎么找了。 然后我们深入init函数体,观察。init里面执行的业务逻辑就是 存储第一排元素的高度,然后重排剩下的元素。 通过columns函数来获得当前窗口最多可以放下多少列,然后设置容器的居中(通过padding设置即可). 接下来,遍历pin的单元框,将第一排的元素高度存放在arrHeight数组里,将剩下的元素进行重排。 其他的函数觉得没什么讲解的必要了。 我们着重来看一下overLoad函数.
2、overLoad

var overLoad = function(ele) {
  var index = getIndex(arrHeight),
   minHeight = Math.min.apply(null, arrHeight), //获取最小高度
   pins = $('.pin'),
   style = ele.style;
  style.position = "absolute";
  style.top = minHeight + "px"; //设置当前元素的高度
  style.left = pins[index].offsetLeft + "px";
  arrHeight[index] += ele.offsetHeight;
 }

在overLoad里面有getIndex函数,获取最小高度的index,然后就可以设置传入进来的ele元素的位置(绝对定位), top为数组中最小高度的px值,left就为第一排制定的Index元素的左边距位置。 最后更新一下高度,ok!!! that's enough.
3、设置加载位置

var dataInt = [{
  'src': '1.jpg'
 }, {
  'src': '2.jpg'
 }, {
  'src': '3.jpg'
 }, {
  'src': '4.jpg'
 }, {
  'src': '1.jpg'
 }, {
  'src': '2.jpg'
 }, {
  'src': '3.jpg'
 }, {
  'src': '4.jpg'
 }];

 function isLoad() { //是否可以进行加载
  var scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
   wholeHeight = document.documentElement.clientHeight || document.body.clientHeight,
   point = scrollTop + wholeHeight; //页面底部距离header的距离
  var arr = $('.pin');
  var lastHei = arr[arr.length - 1].getBoundingClientRect().top;
  return (lastHei < point) ? true : false;
 }
 //处理滑动
 var dealScroll = (function() {
  var main = $('#main')[0],
   flag = true;
  return function() {
   if (isLoad() && flag) {
    for (var i = 0, data; data = dataInt[i++];) {
     var div = document.createElement('div');
     div.innerHTML = temp(data.src);
     div.className = "pin";
     main.appendChild(div);
     overLoad(div); //和上面的overload有耦合性质
    }
    flag = false;
    setTimeout(function() { //控制滑行手速,时间越长对速度的滑动时间影响越大。
     flag = true;
    }, 200);
   }
  }
 })();

 function temp(src) {
  return `
  <div class="box">
   <img src="http://cued.xunlei.com/demos/publ/img/P_00${src}"/>
  </div>
 `;
 }

其实,精华就在上一部分,这个只是作为一个加载数据的手段,当然,你可以点击加载(手动触发),或者其他的加载方法。 当然,怎么设置完全是取决于你的。 所以,随大流,依然是通过下滑滚动加载。 继续,找入口函数->dealScroll. 该函数执行的任务就是,通过isload函数,判断是否可以进行加载判断。 我们看一下isload函数,这个就是滚动加载的关键点.

function isLoad() { //是否可以进行加载
  var scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
   wholeHeight = document.documentElement.clientHeight || document.body.clientHeight,
   point = scrollTop + wholeHeight; //页面底部距离header的距离
  var arr = $('.pin');
  var lastHei = arr[arr.length - 1].getBoundingClientRect().top;
  return (lastHei < point) ? true : false;
 }

通过计算得出,页面底部距视口的位置(工具条下部) 与 最后一个元素的绝对位置比较,如果 滑动距离超过,则启用加载。
yeah~ That's over.
back to dealScroll
接下来就是看加载的部分了,这个部分其实也没什么说的,就是创建一个div节点,然后将他放到容器的最后,并且通过overLoad函数来处理该节点的位置。 另外在该函数的末尾,我设置了一个控制滑动速度的trick,通过对函数的节流,防止有时候,请求过慢,用户重复发送请求,造成资源浪费。
然后,这一部分也可以告一段落了。
4、响应式
最后一部分就是响应式了, 这部分,也超级简单,只要你封装性做的好,其实这一部分就是添加一个resize事件就over了。我们继续找入口函数。

var resize = (function() {
   var flag;
   return function(fn) {
    clearTimeout(flag);
    flag = setTimeout(function() { //函数的节流,防止用户过度移动
     fn();
     console.log("ok")
    }, 500);
   }
})();

同样,这里使用到了函数节流的思想,要知道,作为一个程序员,永远不要以为 用户 干不出什么 傻事. 比如,没事的时候拖着浏览器窗口玩,放大,缩小,再放大... 其实,这事我经常干,因为没有女朋友,写代码写累了,就拖浏览器玩。 所以,考虑到我们单身狗的需求,使用函数节流是非常有必要的。 感兴趣的童鞋,可以参考我前面的文章,进行学习。 说明一下,这里的回调函数指的就是init函数,但是 需要对init做些改动。详见。

var update = function(ele) { //当resize的时候,重新设置
  ele.style.position = "initial";
 }
 //初始化时执行函数
 var init = function() {
   var pins = $(".pin"),
    col = columns();
   arrHeight = []; //清空高度
   setCenter(); //设置包裹容器的宽度
   for (var i = 0, pin; pin = pins[i]; i++) {
    if (i < col) { //存储第一排的高度
     arrHeight.push(pin.offsetHeight);
     update(pin);
    } else {
     overLoad(pin); //将元素的位置重排
    }
   }
  }

上面需要加入update,对新的第一排元素进行更新。
然后就可以直接搬过来使用即可。
这就是绝对是布局的大部分内容了,关于javascript瀑布流另一种布局方式请参考下一篇文章《详解javascript实现瀑布流列式布局》

Javascript 相关文章推荐
在页面中js获取光标/鼠标的坐标及光标的像素坐标
Nov 11 Javascript
javascript的渐进增强与平稳退化浅谈
Nov 12 Javascript
一个JavaScript操作元素定位元素的实例
Oct 29 Javascript
javascript生成大小写字母
Jul 03 Javascript
JavaScript中的数据类型转换方法小结
Oct 26 Javascript
DWR中各种java方法的调用
May 04 Javascript
jQuery中fadein与fadeout方法用法示例
Sep 16 Javascript
JS实现针对给定时间的倒计时功能示例
Apr 11 Javascript
JS实现将二维数组转为json格式字符串操作示例
Jul 12 Javascript
微信小程序动画(Animation)的实现及执行步骤
Oct 28 Javascript
vant自定义二级菜单操作
Nov 02 Javascript
关于React Native 无法链接模拟器的问题
Jun 21 Javascript
理解Javascript文件动态加载
Jan 29 #Javascript
JavaScript操作select元素和option的实例代码
Jan 29 #Javascript
JavaScript学习总结之JS、AJAX应用
Jan 29 #Javascript
Angularjs中UI Router全攻略
Jan 29 #Javascript
JavaScript数据结构与算法之集合(Set)
Jan 29 #Javascript
AngularJS 使用 UI Router 实现表单向导
Jan 29 #Javascript
JavaScript数据结构与算法之链表
Jan 29 #Javascript
You might like
PHP 文件上传进度条的两种实现方法的代码
2007/11/25 PHP
使用PHP把HTML生成PDF文件的几个开源项目介绍
2014/11/17 PHP
将FCKeditor导入PHP+SMARTY的实现方法
2015/01/15 PHP
thinkPHP连接sqlite3数据库的实现方法(附Thinkphp代码生成器下载)
2016/05/27 PHP
PHP 实现页面静态化的几种方法
2017/07/23 PHP
php调用云片网接口发送短信的实现方法
2017/10/25 PHP
用javascript实现的激活输入框后隐藏初始内容
2007/06/29 Javascript
LazyForm jQuery plugin 定制您的CheckBox Radio和Select
2009/10/24 Javascript
JavaScript类库D
2010/10/24 Javascript
防止jQuery ajax Load使用缓存的方法小结
2014/02/22 Javascript
浅谈javascript的分号的使用
2015/05/12 Javascript
JavaScript统计网站访问次数的实现代码
2015/11/18 Javascript
jquery制作属于自己的select自定义样式
2015/11/23 Javascript
理解JavaScript中worker事件api
2015/12/25 Javascript
jQuery模拟select实现下拉菜单功能
2016/06/20 Javascript
js实现带简单弹性运动的导航条
2017/02/22 Javascript
Angular2.0实现modal对话框的方法示例
2018/02/18 Javascript
网页爬虫之cookie自动获取及过期自动更新的实现方法
2018/03/06 Javascript
vue-cli项目优化方法- 缩短首屏加载时间
2018/04/01 Javascript
微信小程序canvas.drawImage完全显示图片问题的解决
2018/11/30 Javascript
基于JS开发微信网页录音功能的实例代码
2019/04/30 Javascript
浅谈laytpl 模板空值显示null的解决方法及简单的js表达式
2019/09/19 Javascript
JS co 函数库的含义和用法实例总结
2020/04/08 Javascript
Vue中keep-alive组件的深入理解
2020/08/23 Javascript
python使用PyV8执行javascript代码示例分享
2013/12/04 Python
Python selenium 父子、兄弟、相邻节点定位方式详解
2016/09/15 Python
Python查找第n个子串的技巧分享
2018/06/27 Python
Python multiprocess pool模块报错pickling error问题解决方法分析
2019/03/20 Python
Python实现时间序列可视化的方法
2019/08/06 Python
什么是CSS3 HSLA色彩模式?HSLA模拟渐变色条
2016/04/26 HTML / CSS
俄罗斯天然和有机产品、健康生活网上商店:Fitomarket.ru
2020/10/09 全球购物
人事经理岗位职责范本
2014/08/04 职场文书
金融保险专业求职信
2014/09/03 职场文书
公司离职证明标准样本
2014/10/05 职场文书
公司股份转让协议书范本
2015/01/28 职场文书
canvas实现贪食蛇的实践
2022/02/15 Javascript