重构项目之二:使用瀑布流效果加载图片

虽然这个功能最后使用了另外的插件,但是还是讲一下大概的原理吧,还是先上图:

 

 

功能描述:

  1. 根据不同菜单的属性值分别加载不同的数据

  2. 下拉滚动条到一定位置预加载图片,滚动条拉到最底下的时候渲染html;

  3. 鼠标移到菜单,切换各个图片列表;

  4. 鼠标移到图片列表上,显示详细信息;

 

技术实现方案:

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

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

 

 

一开始的时候最头疼的是如何定位的问题,后来经过朋友指导终于解决:

  计算总共有多少列图片并且把每一列的高度都放到一个数组里面。每当一张图片加载完成的时候就查找这个数组里面最小的值,并且定位当前图片的top设置为这个值,完成后把这个图片的高度加上数组里面的最小值并且返回到数组里面,依次类推。 

 

PS:因为这个功能代码太多,只能作基本的简单分解代码了: 

 1 // 创建用于记录每列高度的数组
 2 _getLowestCol: function() {
 3     t._cols = new Array(5),min = 0;
 4     // 初始化为0
 5     for (var i = 0; i < t._cols.length; i++) {
 6         if (cols[i] < cols[min]) {
 7             min = i;
 8         }
 9         return min;
10     }
11 },
12 _reposition: function() {
13     t._grids.each(function(i, grid) {
14         //先显示出来
15         grid = $(grid).show();
16         
17         var height = grid.outerHeight(), min = t._getLowestCol();
18 
19         // 定位
20         grid.animate({
21             left: (t._colWidth + t._colSpacing) * min,
22             top: t._cols[min],
23             opacity: 1
24         },1000);
25         // 记录高度
26         t._cols[min] += height;
27     });
28 
29 }
View Code

 

 

其次开发过程中遇到的难题是:

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

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

 1 var isLoad = {};//是否载入过
 2 labelType.mouseover(function() {
 3     var i = $(this).index();
 4     var api = _this.attr('api');//接口标识
 5     
 6     if(! isLoad[ api ]){
 7         isLoad[ api ] = i;
 8         loadData(wrapper, api);
 9     }
10     
11 });
View Code

 

 

以下为全部代码:

html:

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 5 <title></title>
 6 <style type="text/css">
 7 *{margin:0;padding:0;}
 8 ul,li{    list-style-type:none;}
 9 li img{width:100%;list-style:none;}
10 </style>
11 </head>
12 <body>
13     <div class="photo_box">
14         <ul id="container" style="border:1px solid #000;width:80%;height:600px;overflow:hidden;margin:0 auto;position: relative;">
15         </ul>
16         <div id="loading" class="loading" style="text-align: center;margin-top: 20px;font-size: 1.2em;">加载中...</div>
17         <div id="more" class="more"style="text-align: center;margin-top: 20px;font-size: 1.2em;"><input type="button" value="更 多" id="clear" /></div>
18     </div>
19 <script type="text/javascript" src="http://daisy.com/js-learning/resule/_example/waterFall_1.2/waterfall.js"></script>
20 <script type="text/javascript" src="http://x1.xiuimg.com/webjs/lib/jquery/jquery/1.7.2/jquery.js"></script>
21 <script type="text/javascript">
22 
23     waterFall.init({
24         container: $('#container'),
25         dataURL: 'http://www.woxiu.com/index.php?action=Index/Main&do=ApiZhuboGrade',
26         dataType: 'jsonp',
27         colWidth: 200,
28         colSpacing: 10,
29         rowSpacing: 15,
30         page: 1,
31         pageEnd: 8,
32     });
33 
34     // 限制同时展示的页数
35     var loadCounter = 1;
36     function pageNum(){
37         if (loadCounter >= 3) {
38             $('#more').show();
39             $('#loading').hide();
40             return true;
41         } else {
42             loadCounter++;
43             $('#more').hide();
44             $('#loading').show();
45         }
46         return false;
47     }
48     $('#clear').click(function() {
49         loadCounter = 1;
50         waterFall._loadNext();
51     });
52 
53 
54 </script>
55 </body>
View Code

 

 

js:

  1 /**
  2  * 瀑布流布局组件类
  3  * @param {Object} options 组件设置
  4  *        @param {NodeList} options.container 瀑布流容器
  5  *        @param {String} options.dataURL 数据地址
  6  *        @param {String} [options.dataType='jsonp'] 数据类型,json或jsonp
  7  *        @param {String}    options.template 模板编辑
  8  *        @param {Number} [options.colWidth] 图片大小。
  9  *        @param {Number} [options.colSpacing] 列间隔。
 10  *        @param {Number} [options.rowSpacing] 行间隔。
 11  *        @param {Number} [options.page=1] 数据开始页码
 12  *        @param {Number} [options.pageEnd] 数据末尾页码
 13 
 14  * @pageNum() 函数,如果不需要现在加载也是,需要把函数里面的判断去掉。
 15 
 16 
 17  从加载到显示的流程
 18 
 19 1. 加载数据
 20 2. 拼接HTML写入到页面
 21 3. 检查刚刚写入的HTML中的img是否全部加载完成,如果是,进入5、否则进入4
 22 4. 等待图片加载完成
 23 5. 计算每个元素的位置
 24 
 25  */
 26 
 27 
 28 var waterFall = {
 29     init: function(options) {
 30         var t = this;
 31         t._container = options.container;
 32         t._colWidth = options.colWidth;
 33         t._colSpacing = options.colSpacing;
 34         t._rowSpacing = options.rowSpacing;
 35         t.dataURL = options.dataURL;
 36         t.dataType = options.dataType;
 37         t.page = options.page;
 38         t.pageEnd = options.pageEnd;
 39         t._switch = false;
 40 
 41         //计算有几列 总宽度 / (列宽 + 列间隔)
 42         t._totalCols = parseInt(t._container.width() / (t._colWidth + t._colSpacing));
 43 
 44         // 创建用于记录每列高度的数组
 45         t._cols = new Array(t._totalCols);
 46         // 初始化为0
 47         for (var i = 0; i < t._cols.length; i++) {
 48             t._cols[i] = 0;
 49         }
 50 
 51         t._loadingPage = options.page || 0;
 52         t._loadNext(options);
 53 
 54         //下拉滚动条加载
 55         var lastTime = new Date().getTime();
 56 
 57         $(window).scroll(function() {
 58 
 59             if ( !t._switch ) {
 60                 //判断是否滚动过快,在ie下
 61                 var thisTime = new Date().getTime();
 62 
 63                 if (thisTime - lastTime < 50) {
 64                     console.log(thisTime - lastTime);
 65                     lastTime = thisTime;
 66                     return;
 67                 }
 68 
 69                 if ($(window).scrollTop() + $(window).height() >= document.documentElement.scrollHeight) {
 70                     lastTime = thisTime;
 71                     t._loadNext();
 72                 }
 73             }
 74         });
 75     },
 76     //加载器
 77     _loadNext: function(t) {
 78         var t = this;
 79 
 80         t._switch = true;
 81         //请求数据
 82         if (!t.trigger) {
 83             $.ajax({
 84                 url: t.dataURL,
 85                 data: { page: ++t._loadingPage },
 86                 dataType:t.dataType,
 87                 success: function(response){
 88                     
 89                     t.trigger = t._completeLoading(response);
 90 
 91                 },
 92                 error:function(){console.log('Error! 请求有误');}
 93             });    
 94         }
 95         return false;
 96     },
 97     //加载完数据调用此函数
 98     _completeLoading: function(result) {
 99         var t = this;
100         if (t._loadingPage >= t.pageEnd) {
101             $('#more').hide();
102             $('#loading').html('<p>已是最后一页了喔 ^_^ ^_^</p>');
103             return true;
104         }
105         else {
106             //if (!pageNum()) {
107                 t._add(result);
108             //};
109         }
110         
111         return false;
112     },
113     //添加格子
114     _add: function(result) {
115         var t = this, grids = '';
116         var content = '';
117         for(var i = 0; i < 10; i++) {
118             content += '<li style="display: none;">' +
119                 '<img src = "http://tse1.mm.bing.net/th?id=OIP.Md8a5117c45764bb6064156cc2f08a5f4o0&w=135&h=272&c=7&rs=1&qlt=90&o=4&pid=1.9">' +
120             '</li>';
121         }
122 
123         console.log(content);
124         //原始定位
125             t._grids = $(content).css({
126                 position: 'absolute',
127                 left: t._container.width(),
128                 top: t._container.height(), 
129                 width: t._colWidth,
130                 opacity: 0
131             });
132 
133         //把Html添加到容器
134         t._container.append(t._grids);
135 
136         // 执行一次_reposition,如果所有图片都加载完成,该方法返回true,否则返回false
137         if ( !t._reposition() ) {
138             // 有图片未加载完,监听onload和onerror
139             t._grids.find('img').bind('load error', function() {
140                 this.loaded = true;
141                 // 有图片加载完成,再次执行_reposition
142                 if (t._grids) {
143                     t._reposition();
144                 }
145             });
146         }
147     },
148     // 此方法用于获取高度最低的列
149     _getLowestCol: function() {
150         var cols = this._cols, min = 0;
151         for (var i = 1; i < cols.length; i++) {
152             if (cols[i] < cols[min]) {
153                 min = i;
154             }
155         }
156         return min;
157     },
158     //定位
159     _reposition: function() {
160 
161         var t = this, allImgsLoaded = true;
162 
163         // 检测图片是否全部加载完成
164         t._grids.find('img').each(function(i, img) {
165             if (!img.loaded && !img.complete) {
166 
167                 allImgsLoaded = false;
168             }
169             return allImgsLoaded;
170         });
171 
172         if (allImgsLoaded) {
173             t._grids.each(function(i, grid) {
174                 //先显示出来
175                 grid = $(grid).show();
176                 
177                 var height = grid.outerHeight(), min = t._getLowestCol();
178 
179                 // 非第一行的时候,要加上行间隔
180                 if (t._cols[min]) { t._cols[min] += t._rowSpacing; }
181                 // 定位
182                 grid.animate({
183                     left: (t._colWidth + t._colSpacing) * min,
184                     top: t._cols[min],
185                     opacity: 1
186                 },1000);
187                 // 记录高度
188                 t._cols[min] += height;
189             });
190             // 重设外层容器高度为最高列高度
191             t._container.css( 'height', Math.max.apply(Math, t._cols) );
192             t._switch = false;
193             delete t._grids;
194         }
195 
196         return allImgsLoaded;
197     },
198 }
View Code

 

 

 

 

PS:欢迎来我秀欣赏美女直播喔(*^__^*) :http://www.woxiu.com/

 

posted @ 2015-04-28 18:54  带上饼干  阅读(505)  评论(0编辑  收藏  举报