基于jQuery的高可定制的瀑布布局实现V2.0
2014-12-04 19:46
211 查看
1.0实现请参考之前的文章:/article/1780335.html
2.0的实现原理和1.0的差不多,但是与1.0不同的是,不管理具体内容的实现,只关注布局。因此,使用2.0会比1.0要麻烦,但是会更加灵活。
另外,2.0无需再依赖css样式文件,只要引入一个js,所有都搞定。而且支持自定义样式名称以便做高级定制。由于布局的关键样式都是通过css添加到document的,因此只要自定义的样式不使用!important关键字,就不会破坏布局。
具体的改变与支持情况,想见代码内注释说明。
先看效果图(该实例实现了视频类型资源的混入展示,实现了鼠标停留图片显示操作按钮“喜欢”和“收藏”):
使用代码(省略引入,省略自定义样式),这部分就没好好完善了,辛苦点看吧:
JS代码如下:
2.0的实现原理和1.0的差不多,但是与1.0不同的是,不管理具体内容的实现,只关注布局。因此,使用2.0会比1.0要麻烦,但是会更加灵活。
另外,2.0无需再依赖css样式文件,只要引入一个js,所有都搞定。而且支持自定义样式名称以便做高级定制。由于布局的关键样式都是通过css添加到document的,因此只要自定义的样式不使用!important关键字,就不会破坏布局。
具体的改变与支持情况,想见代码内注释说明。
先看效果图(该实例实现了视频类型资源的混入展示,实现了鼠标停留图片显示操作按钮“喜欢”和“收藏”):
使用代码(省略引入,省略自定义样式),这部分就没好好完善了,辛苦点看吧:
<span style="white-space:pre"><script type="text/javascript"></span> <span style="white-space:pre"> </span>var width = 230; var page = 0; var headImage = "http://10.20.53.13/images/files/pdf1.png"; $(document).ready(function(){ var falls = $("#container").falls({ //count: 5, width: width, trim: 50, classes: { content : "content-container" }, end: "<span style=\"width: 100%;margin: 10px;border: 1px solid #efefef; background-color: white;text-align: center;padding: 15px;font-size: 20px;\">加载更多</span>" },loadNext); loadNext(falls); }); function delaySetHeight(img, imgContainer){ var imgHeight = img.height(); if(imgHeight > 0){ imgContainer.height(imgHeight); img.height(imgHeight); }else{ window.setTimeout(function(){ delaySetHeight(img, imgContainer); }, 100); } } function loadContent(row){ var div = $(document.createElement("div")); div.addClass("content-div"); var imgContainer = $(document.createElement("div")); imgContainer.addClass("content-img-div"); var img = $(document.createElement("img")); img.error(function(){ img.attr("src", falls.empty()); }); img.addClass("content-img"); img.load(function(){ delaySetHeight(img, imgContainer); if(row.type === "video"){ var videoIcon = $(document.createElement("img")); videoIcon.attr("src", "images/icons/media_video.png"); videoIcon.addClass("video-icon"); imgContainer.append(videoIcon); } }); img.attr("src", row.src); img.click(function(){ window.open(row.href); }); var toolbar = $(document.createElement("div")); toolbar.addClass("content-toolbar"); toolbar.html("<div class=\"content-toolbar-left\">喜欢</div><div class=\"content-toolbar-right\">收藏</div>"); imgContainer.mouseover(function(){ imgContainer.find(".content-toolbar").show(); }); imgContainer.mouseout(function(){ imgContainer.find(".content-toolbar").hide(); }); imgContainer.append(img); imgContainer.append(toolbar); toolbar.find(".content-toolbar-left").click(function(){ alert(row.id); }); toolbar.find(".content-toolbar-right").click(function(){ alert(row.src); }); div.append(imgContainer); var span = $(document.createElement("span")); span.addClass("content-about"); span.append("<span class=\"content-about-text\">" + "<img src=\"" + headImage + "\" style=\"width: 32px;height: 32px;padding: 2px; border: 1px solid #efefef;\"/>" + "</span>" + "<span class=\"content-about-text\">" + row.text + "</span>") div.append(span); return div; } function loadNext(falls){ $.ajax({ url: "pins.action", dataType: 'html', type: "POST", async:false, data: { start: page * 20, limit: 20 }, success: function(html){ var data = eval(html); if(data.length == 0){ falls.setFinish(function(end){ end.children().html("没有更多数据了"); }); return; } falls.load(data, loadContent); } }); page++; } </script>
JS代码如下:
/** * 瀑布布局 * @author tomtrije * @version 2.0.0 * @update 2014/12/04 V2.0.0 * @since 2014/11/20 V1.0.0 旧版本更新描述省略 * @update_list * V2.0.0 * 重构实现 只负责布局实现,不再处理具体内容 不再依赖CSS文件,关键布局样式动态赋予,并最小化样式设置 * 支持自适应列数,随窗口大小变化而变化 * 支持两边留空 * 支持定义间隙大小 * 支持自动填充,但自动填充最低高度限制下限100 * 支持指定内容渲染动画或实现自定义动画 * 支持渲染内容后回调处理 * 支持自定义布局样式类,实现自定义细节处理,但实现布局关键的css不会被调整 * 支持自定义滚动事件监听,但一般以window为监听对象,不建议更改 * 指定监听时,必须指定监听对象高度(固定高度),作计算翻页参考高度 * 不做图片异常自动替换,但提供空图给外部引用处理异常 * 不解析具体数据,提供解析适配器,由调用程序自行实现解析 * ------------------------------------------------------------------- * @desc 使用示例 * ------------------------------------------------------------------- var page = 0; var pageSize = 20; var falls = $("#container").falls({ width: width, //must trim: 50, //not must end: "加载更多" //not must },loadNext); loadNext(falls); function loadNext(falls){ $.ajax({ url: "pins.action", dataType: 'json', type: "POST", async:false, data: { start: page * pageSize, limit: pageSize }, success: function(data){ if(data.length == 0){ falls.setFinish(function(end){ //TODO: end object of the end document dom // changes end text or content when finish here end.html("没有更多数据了"); }); return; } //TODO: load data and drow documnent here // program will invoke loadContent function to get each content document falls.load(data, loadContent); } }); page++; } function loadContent(row){ //TODO: generate document by row data return dom; } * ------------------------------------------------------------------- * @param $ jQuery * @param window window对象 * @param document document对象 * @param undefined 用于忽略后续参数 */ (function($, window, document, undefined) { //----------定义常量----------begin //允许设置的最低填充高度的下限 var _MIN_LENGTH = 100; //异常显示图,内部不使用,但可以被外部引用实现错误图替换 var _EMPTY_IMG = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"; //----------定义常量----------end //----------定义属性----------begin var options = { count : "auto", //*列数,必须为正数,为auto时自动计算列数 width : 230, //*列宽,必须为正数,必须指定,不能自动计算 split : 5, //*列间隔,必须为正数,必须指定,不能自动计算;实际间隔为指定值*2,即上下左右各有间隔 trim : 0, //两边留白宽度,用于自动计算列数,必须为正数 threshold: 0, //提早加载像素 delay : 20, //延迟填充内容 end : "End", //底部容器内容 //加载内容特效 effect : { mode : "fadeIn", delay : 50 }, classes:{ container : "jquery-falls-container", main : "jquery-falls-main", table : "jquery-falls-table", end: "jquery-falls-end", column : "jquery-falls-column", content : "jquery-falls-content" }, afterrender : $.noop(),//执行完内容渲染后的事件处理 parent : undefined, //布局所属容器,不指定时为创建布局所在dom event : window,//监听滚动事件的window对象,可以指定为其他对象 minHeight: undefined //最低填充高度(>=100),用于设置加载第一页未填满当前窗口时自动加载第二页,以此类推 } var $window = $(window);//window对象 var nextFn = $.noop();//下一页事件 var self;//瀑布布局对象 var doms = {};//缓存dom对象 var count = options.count;//列数 var finish = false;//是否结束 var nexting = false;//是否正在加载下一页 var loading = false;//是否正在渲染 //----------定义属性----------end //----------定义构造函数----------start /** * 定义瀑布布局对象 * @param options 配置 * @returns 当前布局对象 */ $.fn.falls = function(opts, fn) { options = $.extend( { parent : $(this)//整个瀑布布局容器 }, options, opts ); nextFn = fn; self = this; _init(); //----------定义业务私有函数----------start function _init(){ _1_clearDoms(); _2_generateContainers(); _3_generateColumns(); _4_addListeners(); } /** * 清除容器 */ function _1_clearDoms(){ //移除所属容器下其他一切dom,以排除干扰 options.parent.children().remove(); } /** * 创建布局容器 */ function _2_generateContainers(){ //设置所属容器 doms.parent = options.parent; //创建顶级容器 doms.container = $(document.createElement("div")); //创建主容器 doms.main = $(document.createElement("div")); //创建表格容器并赋予样式 doms.table = $(document.createElement("div")); //创建底部监听容器并赋予样式 doms.end = $(document.createElement("div")); doms.end.html(options.end); //设置容器继承关系 doms.main.append(doms.table); doms.main.append(document.createElement("div")); doms.main.append(doms.end); doms.main.append(document.createElement("p")); doms.container.append(doms.main); doms.parent.append(doms.container); //赋予各容器样式 $(doms.container).addClass(options.classes.container); //----main---- $(doms.main).addClass(options.classes.main); $(doms.main).css({ display : "block", textAlign : "center" }); //----table---- $(doms.table).addClass(options.classes.table); $(doms.table).css({ display : "inline-block" }); //----end---- $(doms.end).addClass(options.classes.end); $(doms.end).css({ display : "inline-block", textAlign: "center", margin: 20 }); } /** * 创建列布局 */ function _3_generateColumns(skip){ //计算实际列宽度 var oneColumnWidth = options.width + options.split * 2; if(!skip){ //计算列数 if(options.count == 'auto'){ var w = $window.width(); count = parseInt((w - (options.trim * 2)) / oneColumnWidth); count = count < 1 ? 1 : count; self.resize = _o_resize; }else{ count = options.count; self.resize = $.noop(); } } $(doms.table).width(oneColumnWidth * count); $(doms.end).width(oneColumnWidth * count); doms.columns = new Array(); //创建列 for(var i = 0; i < count; i++){ //创建列 var column = $(document.createElement("div")); //使用相对定位,便于列内做相对于父容器的绝对定位 column.css({ float : "left", display : "inline-block", width: options.width, margin: options.split }); //给列添加样式 column.addClass(options.classes.column); doms.table.append(column); doms.columns.push(column); } } /** * 监听滚动事件 */ function _4_addListeners(){ $(options.event || window).scroll(_next); $(options.event || window).resize(self.resize); } /** * 检验并触发下一页事件 */ function _next(){ if(loading){ return; } if(finish){ return; } if(!belowthefold({ element: doms.end, container: options.event || window, threshold: options.threshold })){ nexting = true; nextFn(self); //当前高度小于自动填充高度,即未填满容器,自动加载下一页内容 }else if(options.minHeight && _getMinHeight() < options.minHeight){ if(nexting){ return; } nexting = true; nextFn(self); }else{ nexting = false;//表示下一次滚动到底部可以触发下一页,同时表示当前已在底部 } } /** * 销毁对象 */ function _destroy(){ $(self).remove(); } /** * 当窗口大小变更时执行的变更 */ function _o_resize(){ //计算列数 var oneColumnWidth = options.width + options.split * 2; var w = $window.width(); self.resetColumn(parseInt((w - (options.trim * 2)) / oneColumnWidth)); } /** * 获取列最低高度 * @returns height 最低高度 */ function _getMinHeight(){ var minHeight = options.minHeight; for(var i = 0; i < doms.columns.length; i++){ minHeight = minHeight > doms.columns[i].height() ? doms.columns[i].height() : minHeight; } return minHeight; } /** * 获取列最大高度 * @returns height 最大高度 */ function _getMaxHeight(){ var maxHeight = 0; for(var i = 0; i < doms.columns.length; i++){ maxHeight = maxHeight < doms.columns[i].height() ? doms.columns[i].height() : maxHeight; } return maxHeight; } /** * 获取高度最低的列,即选择的插入列 */ function _getMinColumn(){ //记录每个列的高度和序号 var lengthArray = new Array(); for(var i = 0; i < doms.columns.length; i++){ var obj = {}; obj.len = doms.columns[i].height(); obj.index = i; obj.dom = doms.columns[i]; lengthArray.push(obj); } //记录最低高度的列 var minIndexs = new Array(); for(var i in lengthArray){ var obj = lengthArray[i]; if(minIndexs.length == 0){ minIndexs.push(obj); }else{ //如果有更低的列,清空缓存的最低高度的列容器数组,放入最新的列容器 if(minIndexs[0].len > obj.len){ minIndexs = new Array(); minIndexs.push(obj); //如果有一样低的列容器,放入数组 }else if(minIndexs[0].len == obj.len){ minIndexs.push(obj); } } } //如果只有一个最低列,直接选择该列,否则从中随机取一列 if(minIndexs.length > 1){ var random = getRandom(minIndexs.length - 1); return minIndexs[random].dom; }else if(minIndexs.length > 0){ return minIndexs[0].dom; } return undefined; } /** * 内容容器加载特效 * @param content */ function _effectContent(content){ switch(options.effect.mode){ case "fadeIn": content.fadeIn(options.effect.delay || 0, function(){ _afterrender(content); }); break; case "fadeTo": content.fadeTo(options.effect.delay || 0, options.effect.opacity || 1, function(){ _afterrender(content); }); break; case "show": content.show(options.effect.delay || 0, function(){ _afterrender(content); }); break; case "slideDown": content.slideDown(options.effect.delay || 0, function(){ _afterrender(content); }); break; case "slideUp": content.slideUp(options.effect.delay || 0, function(){ _afterrender(content); }); break; case "animate": content.animate(options.effect.properties, options.effect.options, function(){ _afterrender(content); }); break; } } /** * 渲染回调 * @param content 内容dom对象 */ function _afterrender(content){ if($.isFunction(options.afterrender)){ options.afterrender(content, content.prop("org-data")); } } //----------定义业务私有函数----------end //----------定义公共函数----------start /** * 获取空图片 * @returns 空图片 */ this.empty = function(){ return _EMPTY_IMG; } /** * 设置结束内容 * @param fn 处理结束 */ this.setFinish = function(fn){ finish = true; if(fn){ fn(doms.end); } } /** * 手工出发下一页事件 */ this.next = function(){ nexting = false; _next(); } /** * 加载一页内容 * @param data 一页数据 * @pram fn 用于建立内容dom的函数,由外部自动根据内容生成 */ this.load = function(data, fn){ if(finish){ return; } if($.isEmptyObject(data)){ return; } //数组反转 ,避免pop时倒序显示 var i = new Array(); while(data.length > 0){ i.push(data.pop()); } data = i; doms.end.hide(); loading = true; (function appendContent(){ //如果内容已经取空表示当前页面加载完毕,判断是否需要继续加载下一页 if(data.length == 0){ loading = false; doms.end.show(); _next(); //doms.table.height(_getMaxHeight()); return; } var row = data.pop(); //内容无效跳过该内容渲染,执行下一步 if($.isEmptyObject(row)){ window.setTimeout(appendContent,options.delay); return; } //获取插入内容 var dom = fn(row); //获取插入列 var column = _getMinColumn(); //生成内容容器 var content = $(document.createElement("div")); //缓存原始数据 content.prop("org-data", row); //建立内容容器继承关系 content.append(dom); //设置内容容器样式 content.addClass(options.classes.content); content.css({ display : "block", position: "relative", width: options.width, marginTop: options.split * 2, marginBottom: options.split * 2 }); content.hide(); column.append(content); _effectContent(content); //延迟加载下一内容 content.ready(function(){ window.setTimeout(appendContent,options.delay); }); })(); return self; } /** * 重新设置列数 * 用于实现窗口大小变更时重新计算列数并重新排布内容 * @param n 列数 */ this.resetColumn = function(n){ if(count == n){ return; } //设置调整列数 count = n; count = count < 1 ? 1 : count; var contents = []; var heights = []; //生成预备内容数组和高度数组 for(var i = 0; i < count; i++){ contents[i] = ""; heights[i] = 0; } /** * 获取高度最低的列 */ function getInsertContentIndex(){ //缓存最低高度列 var minHeight = new Array(); for(var i = 0; i < heights.length; i++){ if(minHeight.length == 0){ minHeight.push({index :i, height : heights[i]}); }else{ //如果有更低的列,清空缓存的最低高度的列容器数组,放入最新的列容器 if(minHeight[0].height > heights[i]){ minHeight = new Array(); minHeight.push({index :i, height : heights[i]}); //如果有一样低的列容器,放入数组 }else if(minHeight[0].height == heights[i]){ minHeight.push({index :i, height : heights[i]}); } } } //如果只有一个最低列,直接选择该列,否则从中随机取一列 if(minHeight.length > 1){ var random = getRandom(minHeight.length - 1); return minHeight[random].index; }else if(minHeight.length == 0){ return 0; }else{ return minHeight[0].index; } } //获取现有内容 var contentDoms = $("." + options.classes.content); //重新计算内容排布 for(var i = 0; i < contentDoms.length; i++){ var index = getInsertContentIndex(); var content = contentDoms.eq(i)[0].outerHTML; heights[index] += contentDoms.eq(i).height(); contents[index] += content; } //清除列内容 $("." + options.classes.columns).remove(); //重新渲染列 _3_generateColumns(true); //向列中添加内容 for(var i = 0;i < contents.length; i++){ var col = doms.columns[i]; col.append(contents[i]); } //渲染后检查是否可以触发下一页 _next(); return self; } //----------定义公共函数----------end return this; } //----------定义构造函数----------end //----------定义工具私有函数----------start /** * 获取随机数 * @param n 最大数 * @returns 随机数 */ function getRandom(n){ return Math.floor(Math.random()*n+1) } /** * 判断内容是否还在容器视野底下 * 进入容器时触发加载事件 * @param settings 参数 * @returns boolean true 还在容器视野底下 false 已进入容器视野 */ function belowthefold(sets) { var fold; if (sets.container === undefined || sets.container === window) { fold = (window.innerHeight ? window.innerHeight : $window.height()) + $window.scrollTop(); } else { fold = $(sets.container).offset().top + $(sets.container).height(); } //如果内容顶部减去预载高度大于容器底部,说明还在可以不用加载 return fold <= $(sets.element).offset().top - sets.threshold; } //----------定义工具私有函数----------end })(jQuery, window, document);
相关文章推荐
- 基于jQuery的高可定制的瀑布布局实现
- 基于jquery实现可定制的web在线富文本编辑器附源码下载
- jQuery插件实现瀑布留布局masonry + infinitescroll 图片高度处理
- 基于jquery实现可定制的web在线富文本编辑器附源码下载
- 基于jquery实现瀑布流布局
- 基于jquery实现瀑布流布局
- 基于jQuery插件imgAreaSelect和ArcGIS server模仿实现百度地图的截图功能
- 基于Silverlight 实现的Easypaas按需定制平台2.0介绍
- 基于Java的界面布局DSL的设计与实现
- 基于Java的界面布局DSL的设计与实现
- jQuery应用-实现博客个性主页布局拖拽功能
- 好友选择器V2.0--基于Jquery
- 基于Jquery 好友选择器V2.0
- 基于jquery组件实现的自动完成控件(asp.net服务器控件)
- 基于jQuery的ajax功能实现web service的json转化
- 基于jQuery的ajax功能实现web service的json转化
- 基于jQuery的好友选择器V2.0
- 基于Java的界面布局DSL的设计与实现
- 使用js实现基于可视布局信息的网页噪音去除的测试方法
- 基于GoogleMap,Mapabc,51ditu,VirtualEarth,YahooMap Api接口的Jquery插件的通用实现(含源代码下载)