javascript实现瀑布流加载图片原理


Posted in Javascript onFebruary 02, 2016

讲一下大概的原理吧,还是先上图:

 javascript实现瀑布流加载图片原理

 功能描述:

  • 根据不同菜单的属性值分别加载不同的数据
  • 下拉滚动条到一定位置预加载图片,滚动条拉到最底下的时候渲染html;
  • 鼠标移到菜单,切换各个图片列表;
  • 鼠标移到图片列表上,显示详细信息; 

技术实现方案:

先梳理一下从加载到显示的流程:

1. 加载数据

2. 拼接HTML写入到页面

3. 检查刚刚写入的HTML中的img是否全部加载完成,如果是,进入5、否则进入4

4. 等待图片加载完成

5. 计算每个元素的位置

一开始的时候最头疼的是如何定位的问题,后来经过朋友指导终于解决:计算总共有多少列图片并且把每一列的高度都放到一个数组里面。每当一张图片加载完成的时候就查找这个数组里面最小的值,并且定位当前图片的top设置为这个值,完成后把这个图片的高度加上数组里面的最小值并且返回到数组里面,依次类推。 
 PS:因为这个功能代码太多,只能作基本的简单分解代码了: 

// 创建用于记录每列高度的数组
_getLowestCol: function() {
  t._cols = new Array(5),min = 0;
  // 初始化为0
  for (var i = 0; i < t._cols.length; i++) {
    if (cols[i] < cols[min]) {
      min = i;
    }
    return min;
  }
},
_reposition: function() {
  t._grids.each(function(i, grid) {
    //先显示出来
    grid = $(grid).show();
    
    var height = grid.outerHeight(), min = t._getLowestCol();

    // 定位
    grid.animate({
      left: (t._colWidth + t._colSpacing) * min,
      top: t._cols[min],
      opacity: 1
    },1000);
    // 记录高度
    t._cols[min] += height;
  });

}

 其次开发过程中遇到的难题是:因为如上图所示,鼠标移动到菜单栏需要切换图片列表,并且分别需要用瀑布流加载不同类型的数据。所以要处理在切换页面的时候如何才能做到每个页面只执行一次代码请求接口,而不需要每一次切换都重新请求数据接口,仅仅执行切换显示图片列表的操作就可以了。

考虑到每一个菜单都有一个自定义属性,所以这个问题轻易地解决了:建立一个对象来记录当前菜单是否已经执行过代码,如果没有就执行请求数据 。 

var isLoad = {};//是否载入过
labelType.mouseover(function() {
  var i = $(this).index();
  var api = _this.attr('api');//接口标识
  
  if(! isLoad[ api ]){
    isLoad[ api ] = i;
    loadData(wrapper, api);
  }
  
});

以下为全部代码:
html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<style type="text/css">
*{margin:0;padding:0;}
ul,li{  list-style-type:none;}
li img{width:100%;list-style:none;}
</style>
</head>
<body>
  <div class="photo_box">
    <ul id="container" style="border:1px solid #000;width:80%;height:600px;overflow:hidden;margin:0 auto;position: relative;">
    </ul>
    <div id="loading" class="loading" style="text-align: center;margin-top: 20px;font-size: 1.2em;">加载中...</div>
    <div id="more" class="more"style="text-align: center;margin-top: 20px;font-size: 1.2em;"><input type="button" value="更 多" id="clear" /></div>
  </div>
<script type="text/javascript" src="../../lib/seajs/sea.js"></script>
<script type="text/javascript" src="../../lib/base/1.0.x/base.js"></script>
<script type="text/javascript">
seajs.use(['lib/jquery/1.11.x/index.js', '_example/waterFall_1.1/waterfall.js'], function($, waterFall) {
  waterFall.init({
    container: $('#container'),
    dataURL: 'http://www.woxiu.com/index.php?action=Index/Main&do=ApiZhuboGrade',
    dataType: 'jsonp',
    template: '<% for (var i = 0; i < data.length; i++) { %>' +
            '<li style="display: none;">' +
              '<img src=" <%-data[i].room_img%> ">' +
            '</li>' + 
          '<% } %>',
    colWidth: 200,
    colSpacing: 10,
    rowSpacing: 15,
    page: 1,
    pageEnd: 8,
  });

  // 限制同时展示的页数
  var loadCounter = 1;
  function pageNum(){
    if (loadCounter >= 3) {
      $('#more').show();
      $('#loading').hide();
      return true;
    } else {
      loadCounter++;
      $('#more').hide();
      $('#loading').show();
    }
    return false;
  }
  $('#clear').click(function() {
    loadCounter = 1;
    waterFall._loadNext();
  });
});

</script>
</body>

js:

/**
 * 瀑布流布局组件类
 * @param {Object} options 组件设置
 *    @param {NodeList} options.container 瀑布流容器
 *    @param {String} options.dataURL 数据地址
 *    @param {String} [options.dataType='jsonp'] 数据类型,json或jsonp
 *    @param {String}  options.template 模板编辑
 *    @param {Number} [options.colWidth] 图片大小。
 *    @param {Number} [options.colSpacing] 列间隔。
 *    @param {Number} [options.rowSpacing] 行间隔。
 *    @param {Number} [options.page=1] 数据开始页码
 *    @param {Number} [options.pageEnd] 数据末尾页码

 * @pageNum() 函数,如果不需要现在加载也是,需要把函数里面的判断去掉。


 从加载到显示的流程

1. 加载数据
2. 拼接HTML写入到页面
3. 检查刚刚写入的HTML中的img是否全部加载完成,如果是,进入5、否则进入4
4. 等待图片加载完成
5. 计算每个元素的位置

 */
define(function(require, exports, module) {
  'use strict';
  
  var Tmpl = require('lib/tmpl/2.1.x/index.js'),
    $ = require('lib/jquery/1.11.x/index.js');

  var waterFall = {
    init: function(options) {
      var t = this;
      t._container = options.container;
      t._template = options.template;
      t._colWidth = options.colWidth;
      t._colSpacing = options.colSpacing;
      t._rowSpacing = options.rowSpacing;
      t.dataURL = options.dataURL;
      t.dataType = options.dataType;
      t.page = options.page;
      t.pageEnd = options.pageEnd;
      t._switch = false;

      //计算有几列 总宽度 / (列宽 + 列间隔)
      t._totalCols = parseInt(t._container.width() / (t._colWidth + t._colSpacing));

      // 创建用于记录每列高度的数组
      t._cols = new Array(t._totalCols);
      // 初始化为0
      for (var i = 0; i < t._cols.length; i++) {
        t._cols[i] = 0;
      }

      t._loadingPage = options.page || 0;
      t._loadNext(options);

      //下拉滚动条加载
      var lastTime = new Date().getTime();

      $(window).scroll(function() {

        if ( !t._switch ) {
          //判断是否滚动过快,在ie下
          var thisTime = new Date().getTime();

          if (thisTime - lastTime < 50) {
            console.log(thisTime - lastTime);
            lastTime = thisTime;
            return;
          }

          if ($(window).scrollTop() + $(window).height() >= document.documentElement.scrollHeight) {
            lastTime = thisTime;
            t._loadNext();
          }
        }
      });
    },
    //加载器
    _loadNext: function(t) {
      var t = this;

      t._switch = true;
      //请求数据
      if (!t.trigger) {
        $.ajax({
          url: t.dataURL,
          data: { page: ++t._loadingPage },
          dataType:t.dataType,
          success: function(response){
            
            t.trigger = t._completeLoading(response);

          },
          error:function(){console.log('Error! 请求有误');}
        });  
      }
      return false;
    },
    //加载完数据调用此函数
    _completeLoading: function(result) {
      var t = this;
      if (t._loadingPage >= t.pageEnd) {
        $('#more').hide();
        $('#loading').html('<p>已是最后一页了喔 ^_^ ^_^</p>');
        return true;
      }
      else {
        //if (!pageNum()) {
          t._add(result);
        //};
      }
      
      return false;
    },
    //添加格子
    _add: function(result) {
      var t = this, grids = '';
      //调用模板
      var content = Tmpl.render(t._template, {data:result.data});
      //原始定位
        t._grids = $(content).css({
          position: 'absolute',
          left: t._container.width(),
          top: t._container.height(), 
          width: t._colWidth,
          opacity: 0
        });

      //把Html添加到容器
      t._container.append(t._grids);

      // 执行一次_reposition,如果所有图片都加载完成,该方法返回true,否则返回false
      if ( !t._reposition() ) {
        // 有图片未加载完,监听onload和onerror
        t._grids.find('img').bind('load error', function() {
          this.loaded = true;
          // 有图片加载完成,再次执行_reposition
          if (t._grids) {
            t._reposition();
          }
        });
      }
    },
    // 此方法用于获取高度最低的列
    _getLowestCol: function() {
      var cols = this._cols, min = 0;
      for (var i = 1; i < cols.length; i++) {
        if (cols[i] < cols[min]) {
          min = i;
        }
      }
      return min;
    },
    //定位
    _reposition: function() {

      var t = this, allImgsLoaded = true;

      // 检测图片是否全部加载完成
      t._grids.find('img').each(function(i, img) {
        if (!img.loaded && !img.complete) {

          allImgsLoaded = false;
        }
        return allImgsLoaded;
      });

      if (allImgsLoaded) {
        t._grids.each(function(i, grid) {
          //先显示出来
          grid = $(grid).show();
          
          var height = grid.outerHeight(), min = t._getLowestCol();

          // 非第一行的时候,要加上行间隔
          if (t._cols[min]) { t._cols[min] += t._rowSpacing; }
          // 定位
          grid.animate({
            left: (t._colWidth + t._colSpacing) * min,
            top: t._cols[min],
            opacity: 1
          },1000);
          // 记录高度
          t._cols[min] += height;
        });
        // 重设外层容器高度为最高列高度
        t._container.css( 'height', Math.max.apply(Math, t._cols) );
        t._switch = false;
        delete t._grids;
      }

      return allImgsLoaded;
    },
  }

  return waterFall;
});

以上就是本文的全部内容,希望对大家学习javascript程序设计有所帮助。

Javascript 相关文章推荐
js判断变量是否空值的代码
Oct 26 Javascript
js中function()使用方法
Dec 24 Javascript
js中replace的用法总结
Dec 27 Javascript
Javascript限制网页只能在微信内置浏览器中访问
Nov 09 Javascript
Javascript基础教程之数据类型 (布尔型 Boolean)
Jan 18 Javascript
鼠标事件的screenY,pageY,clientY,layerY,offsetY属性详解
Mar 12 Javascript
用js实现简单算法的实例代码
Sep 24 Javascript
清除浏览器缓存的几种方法总结(必看)
Dec 09 Javascript
基于Vue.js 2.0实现百度搜索框效果
Dec 28 Javascript
JavaScript简单实现合并两个Json对象的方法示例
Oct 16 Javascript
vue element-ui 绑定@keyup事件无效的解决方法
Mar 09 Javascript
全面解析JavaScript Module模式
Jul 24 Javascript
基于JQuery实现图片轮播效果(焦点图)
Feb 02 #Javascript
Hammer.js+轮播原理实现简洁的滑屏功能
Feb 02 #Javascript
JavaScript实现的MD5算法完整实例
Feb 02 #Javascript
javascript禁止超链接跳转的方法
Feb 02 #Javascript
JS组件Bootstrap Table使用方法详解
Feb 02 #Javascript
Node.js实现JS文件合并小工具
Feb 02 #Javascript
体验jQuery和AngularJS的不同点及AngularJS的迷人之处
Feb 02 #Javascript
You might like
提问的智慧
2006/10/09 PHP
Apache设置虚拟WEB
2006/10/09 PHP
php提高网站效率的技巧
2015/09/29 PHP
php的instanceof和判断闭包Closure操作示例
2020/01/26 PHP
php判断某个方法是否存在函数function_exists (),method_exists()与is_callable()区别与用法解析
2020/04/20 PHP
让JavaScript 轻松支持函数重载 (Part 1 - 设计)
2009/08/04 Javascript
return false,对阻止事件默认动作的一些测试代码
2010/11/17 Javascript
JQuery扩展插件Validate 5添加自定义验证方法
2011/09/05 Javascript
jquery跨域请求示例分享(jquery发送ajax请求)
2014/03/25 Javascript
Jquery判断radio、selelct、checkbox是否选中及获取选中值方法总结
2015/04/15 Javascript
浅谈json取值(对象和数组)
2016/06/24 Javascript
Actionscript与javascript交互实例程序(修改)
2016/09/22 Javascript
微信小程序 条件渲染详解
2016/10/09 Javascript
详解ECMAScript6入门--Class对象
2017/04/27 Javascript
React从react-router路由上做登陆验证控制的方法
2018/05/10 Javascript
jQuery阻止事件冒泡实例分析
2018/07/03 jQuery
javascript实现函数柯里化与反柯里化过程解析
2019/10/08 Javascript
js实现简单进度条效果
2020/03/25 Javascript
[01:27]2014DOTA2展望TI 剑指西雅图IG战队专访
2014/06/30 DOTA
深入学习Python中的装饰器使用
2016/06/20 Python
Python利用Beautiful Soup模块创建对象详解
2017/03/27 Python
Python sklearn KFold 生成交叉验证数据集的方法
2018/12/11 Python
Python使用pyyaml模块处理yaml数据
2020/04/14 Python
Selenium 配置启动项参数的方法
2020/12/04 Python
纯CSS3实现地球自转实现代码(图文教程附送源码)
2012/12/26 HTML / CSS
班会关于环保演讲稿
2013/12/29 职场文书
兰兰过桥教学反思
2014/02/08 职场文书
销售人员求职的自我评价分享
2014/03/15 职场文书
祖国在我心中演讲稿400字
2014/05/04 职场文书
安全施工标语
2014/06/07 职场文书
小学生三分钟演讲稿
2014/08/18 职场文书
团支部组织委员竞选稿
2015/11/21 职场文书
党员读书活动心得体会
2016/01/14 职场文书
人身损害赔偿协议书
2016/03/22 职场文书
如何解决.cuda()加载用时很长的问题
2021/05/24 Python
唤醒紫霞仙子,携手再游三界!大话手游X《大话西游》电影合作专属剧情任务
2022/04/03 其他游戏