4个小时实战木桶布局
栏目: JavaScript · 发布时间: 6年前
内容简介:最近公司有个项目要用到木桶布局,找了很多插件都感觉不合适,于是决定自己撸一个。已经开源到github。(文章写的匆忙,有错别字或bug请指正)什么是木桶布局,我们随便百度一张图片就知道了。如下图。那么问题来了,要怎么实现这种效果呢?本人想了一种思路,看下图。
最近公司有个项目要用到木桶布局,找了很多插件都感觉不合适,于是决定自己撸一个。已经开源到github。(文章写的匆忙,有错别字或bug请指正)
什么是木桶布局,我们随便百度一张图片就知道了。如下图。
那么问题来了,要怎么实现这种效果呢?本人想了一种思路,看下图。
首先排列若干图片,用一个基准高度来设置他们,让后当放不下的时候在进行整体缩放。
实现
让我们愉快的coding吧~,准备把它封装成一个jQ插件。考虑设计以下方法
- loader 图片加载器
- render 图片渲染器
- template 模板渲染器
- resize 重新排版器
- loadMore 动态加载图片
那么让我们创建iboot.js。
;(function ($, root) { function Iboot() { } })(jQuery, window); 复制代码
这样实现了一个简单的jq插件模板。由于某些参数需要传递过来,我们要继续完善。
function Iboot(ele, config) { this.ele = $(ele) this.eleWidth = this.ele.width() this.config = $.extend({ // 这里会合并用户传来的参数 baseHeight: 400, // 默认基准高度 list: [ // 定义图片格式 { src: '', alt: 'xxx' } ], template: function (dom) { return dom }, scrollBox: $(document.body), afterLoad: function () { //加载之前回调 }, beforeLoad: function () { //加载之后回调 } }, config) this.innerData = { //内置的储存器 nowItemWidth: 0, //现在的增加长度 appendDoms: [], scrollBoxData: { }, saveDom: [], groupid: 0 } } 复制代码
实现图片加载器
首先我们要实现图片加载器,代码如下。
Iboot.prototype.loader = function(list, success, error, done){ var now = 0; $.each(list, function (i, v) { (function (i, v) { var img = new Image() img.onload = function () { success(v, img) now++ if(now === list.length) { done() } } img.onerror = function (err) { error(v, img, err) now++ if(now === list.length) { done() } } img.src = v.src img.alt = v.alt })(i, v); }) } 复制代码
加载传入的list,加载后分别指派给回调函数 success(成功之后)error(加载失败)done(全部加载完成)
实现模板函数
Iboot.prototype.template = function(src, alt){ var item = $('<div class="iboot-item" style="float: left"></div>'); item.append( $('<img src="'+src+'" alt="'+alt+'" style="width: 100%;height: auto;">') ); return this.config.template( item ) } 复制代码
这和函数允许使用用户传来的回调函数在渲染之前操作一下模板,用来DIYdom,比如(加边距,加文字等等)
【核心】实现render渲染函数
Iboot.prototype.render = function(cp, img){ var _this = this // 获取一下item模板 var dom = this.template(cp.src, cp.alt) // 获取加载后的图片的比例 var scale = img.width / img.height // 给item加属性,保存当前信息,用给Resize的时候读取,根据信息重新排版 dom.attr({ 'data-rew': img.width, 'data-reh': img.height, 'data-scale': scale }) // 计算根据基准高度缩放之后的信息 var comp = { width: scale * this.config.baseHeight, height: this.config.baseHeight, } // nowItemWidth的作用就是没次加元素,就把宽度累加,然后当累加的宽度大于父盒子的宽度的时候,进行整体缩放,缩放完成之后重置为0 this.innerData.nowItemWidth += comp.width // appendDoms储存当前列表,缩放之后清空 this.innerData.appendDoms.push(dom) // saveDom是全局的,所有信息都储存在这,用于之后resize this.innerData.saveDom.push(dom) // 缩放当前dom dom.css({ height: comp.height, width: comp.width }) // 添加到父盒子 this.ele.append(dom) var compW = this.innerData.nowItemWidth - this.ele.width() // 当放不下的时候,进行整体缩放 if(compW > 0) { // 超出大小整体缩放 var nowScale = this.innerData.nowItemWidth / this.getMediaHeight() var scaleHeight = this.ele.width() / nowScale // 整体缩放 $.each(this.innerData.appendDoms, function (i, v) { var data = $(v).data() $(v).css({ height: scaleHeight, width: data.scale * scaleHeight }) // 对缩放后的元素进行分组,这个分组很重要,因为当图片小于排版要求的时候就不会分组,不分组的就隐藏掉。 $(v).attr('group', _this.innerData.groupid) }) this.innerData.nowItemWidth = 0 this.innerData.appendDoms = [] this.innerData.groupid++ } } 复制代码
总结一下render的思路
- 累计添加子元素,把等比例缩放后的宽度储存起来
- 当放不下的时候进行整体缩放
- 请空储存的值
- 以此类推
关于 groupid
,因为图片会出现不符合排版要求的情况,我们不对其进行分组,隐藏它们。当resize或者loadmore的时候符合分组要求在显示它们。
实现resize排版器
resize的思想和render很像,只不过就是考虑了groupid的显示隐藏。
Iboot.prototype.resize = function(){ var baseHeight = this.getMediaHeight() var _this = this $.each(this.innerData.saveDom, function (i, v) { // 首先删除所有的group id $.each(_this.innerData.appendDoms, function (i, v) { $(v).removeAttr('groupid') $(v).show() }) var data = $(v).data() var cof = { height: baseHeight, width: baseHeight * data.scale } _this.innerData.appendDoms.push(v) _this.innerData.nowItemWidth += cof.width var compW = _this.innerData.nowItemWidth - _this.ele.width() if(compW > 0) { // 超出大小整体缩放 var nowScale = _this.innerData.nowItemWidth / _this.getMediaHeight() var scaleHeight = _this.ele.width() / nowScale $.each(_this.innerData.appendDoms, function (i, v) { var data = $(v).data() $(v).css({ height: scaleHeight, width: data.scale * scaleHeight }) $(v).attr('group', _this.innerData.groupid) }) _this.innerData.nowItemWidth = 0 _this.innerData.appendDoms = [] _this.innerData.groupid++ } }) _this.innerData.nowItemWidth = 0 _this.innerData.appendDoms = [] _this.innerData.groupid = 0 // 隐藏没有group id 的 $.each(this.innerData.saveDom, function (k, v) { var data = $(v).attr('group') if(!data) { $(v).hide() } }) } 复制代码
loadMore加载更多
Iboot.prototype.loadMore = function(list){ var _this = this // 把上次隐藏groupid的加进去 var coc = [] $.each(this.innerData.saveDom, function (i, v) { var data = $(v).attr('group') if(!data) { if(!v) { return } var img = $(v).find('img') coc.push({ src: img.attr('src'), alt: img.attr('alt'), }) _this.innerData.saveDom.splice(i, 1) } }) list = coc.concat(list) this.beforeLoad() this.loader(list, function (cp, img) { _this.render(cp, img) }, function (cp, img, error) { }, function () { _this.afterLoad() }) } 复制代码
最后把方法抛出到全局
root.Iboot = function(ele, config){ return new Iboot(ele, config).init(); } 复制代码
大功告成,前前后后大概用了4个多小时,头发要掉光了。。。学无止境,告辞!
以上所述就是小编给大家介绍的《4个小时实战木桶布局》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 「Flask实战」鱼书项目实战一
- 「Flask实战」鱼书项目实战三
- 「Flask实战」鱼书项目实战四
- 「Flask实战」鱼书项目实战六
- RocketMQ实战系列从理论到实战
- 「Flask实战」flask鱼书项目实战二
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从0开始做运营 入门篇
张亮 / 4.99元
此书是《从零开始做运营》系列的入门篇。 在互联网产品经理热的今天,关于传统的网站与产品运营的书籍一直非常缺乏,很多有志于互联网行业的年轻人并不明白一款产品、一个网站的策划、上线、成长、成熟直到衰落的过程中,除了产品和网站本身的设计之外,还有一块非常重要的工作是针对网站与产品生命周期的持续运营。 网站与产品运营是一个非常辛苦而非常有趣的事情,希望本书可以为有志于从事互联网网站与产品运营的......一起来看看 《从0开始做运营 入门篇》 这本书的介绍吧!