【js 实践】js 实现木桶布局
还有两个月左右就要准备实习了,所以特意练一练,今天终于搞定了js 的木桶布局了
这一个是按照一个插件的规格去写的以防以后工作需要,详细的解释在前端网这里 http://www.qdfuns.com/notes/37573/535e6e8bf4a6ab06823943628936de87.html
这里只出示一下代码了啦,如果有什么不足的地方请指出无尽感激:
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo2</title>
</head>
<body>
<div class="container">
<div class="gallary-item-inner"><img src="data:images/1.jpg" alt=""></div>
<div class="gallary-item-inner"><img src="data:images/2.jpg" alt=""></div>
<div class="gallary-item-inner"><img src="data:images/3.jpg" alt=""></div>
<div class="gallary-item-inner"><img src="data:images/4.jpg" alt=""></div>
<div class="gallary-item-inner"><img src="data:images/5.jpg" alt=""></div>
<div class="gallary-item-inner"><img src="data:images/6.jpg" alt=""></div>
<div class="gallary-item-inner"><img src="data:images/7.jpg" alt=""></div>
<div class="gallary-item-inner"><img src="data:images/8.jpg" alt=""></div>
<div class="gallary-item-inner"><img src="data:images/9.jpg" alt=""></div>
<div class="gallary-item-inner"><img src="data:images/10.jpg" alt=""></div>
<div class="gallary-item-inner"><img src="data:images/11.jpg" alt=""></div>
<div class="gallary-item-inner"><img src="data:images/12.jpg" alt=""></div>
</div> <div class="add">+</div>
<link rel="stylesheet" type="text/css" href="css/normal.css">
<link rel="stylesheet" type="text/css" href="css/demo2.css">
<script type="text/javascript" src="js/demo2.js"></script>
<script type="text/javascript">
var container = document.querySelectorAll('.container')[],
items = document.querySelectorAll('.gallary-item-inner img'),
addBtn = document.querySelectorAll('.add')[],
barrel = null; window.addEventListener('load', function() {
barrel = new BarrelLayout(container, items, );
}); addBtn.addEventListener('click', function() { var frag = document.createDocumentFragment();
for (var i = ; i < ; i++) {
var num = Math.floor(Math.random() * + ),
path = 'images/' + num + '.jpg';
var div = document.createElement('div'),
img = document.createElement('img'); img.src = path;
div.appendChild(img);
div.className = 'gallary-item-inner';
frag.appendChild(div);
}
container.appendChild(frag);
barrel.refresh(document.querySelectorAll('.gallary-item-inner img')); });
</script>
</body>
</html>
css:
.gallary .gallary-item{
font-size:;
}
.gallary .gallary-item .gallary-item-inner{
display: inline-block;
font-size:;
}
.barrel-container .barrel-row{
line-height:;
text-align: left;
}
.add{
position: fixed;
bottom: 80px;
right: 50px;
width: 80px;
height: 80px;
line-height: 80px;
border-radius: 50%;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
text-align: center;
cursor: pointer;
}
js
/*
木桶布局类
参数:
wrapper:
类型: HTMLElement
描述: 木桶布局的初始化包裹层由用户自己定义 items:
类型: HTMLElementList
描述: 需要木桶布局的子项 baseHeight:
类型: Integer
描述: 用户规定木桶布局每一行的基础高度 实际输出时会与这个高度有差异 流程:
计算 ==> 整理 ==> 渲染dom 属性:
barrelWrap:
类型: HTMLElement
描述: 保存用户传进来的外包裹层 items:
类型: HTMLElementList
描述: 保存用户传进来的所有需要木桶布局的子元素集 baseHeight:
类型: Integer
描述: 接收用户设置的基本高度 lastRow:
类型: Array
描述: 保存上一次渲染木桶布局后最后一行的元素 lastItemIndex:
类型: Integer 方法:
calc:
参数:
itemList: (Array) 接收需要木桶布局的dom 元素列
返回值: Object
{
elemArr: 二维数组,保存每一行应有的元素
rowHeightArr: 一维数组,保存每一行的实际宽度
}
类型: HTMLElementList
描述: 将需要进行木桶布局的 dom 元素(通常是img) 传入这个函数中
首先会根据元素的宽高比例计算出每一个元素按比例缩放的宽度
根据宽度计算出一行能够放入多少个元素
当计算完一行应有元素时再计算出该行的高度, 公式为:
该行所有元素宽度总和 / 用户定义的基本高 = 该行在浏览器显示的宽度 / y
y 为最后的运算结果
最后,将每一个木桶布局元素的高度设置为 y 其宽度总和便会自动填充满整行 render:
参数: rowsArr: (Array) 每一行需要渲染的dom 元素
rowHeightArr: (Array) 每一行的实际行高
返回值: void
描述: 对calc 方法返回的数据进行渲染dom
此方法会首先判断有没有 class="barrel-container" 这个元素存在
如果不存在证明是第一次初始化
为用户指定的 wrapper 元素下生成一个ul类名为barrel-container
在container 下面输出dom 如果barrel-container 存在证明是重新渲染
那么将最后一个li 移除再紧接着输出 init:
参数: null
返回值: void
描述: 调用上面两个方法渲染dom refresh:
参数: newItemsList: (HTMLElemsList)
描述: 为了减少dom 的渲染
当从服务器将加载图片添加到包裹层中时可调用此方法
此方法会根据上一次渲染后的 lastItemIndex 对新元素数组进行切割
切割完成后和上一次渲染后的最后一行元素列合并组成新的渲染数组
之后依次调用 calc() render() 完成输出
*注意一点:
上一次渲染后最后一行的元素已经设置好了高度所以在计算前要将
最后一行的元素清空样式防止布局错乱 */ function BarrelLayout(wrapper, items, baseHeight) {
this.barrelWrap = wrapper;
this.items = items;
this.baseHeight = baseHeight;
// 下面是函数附带的属性
this.lastRow = []; // 保存上一次加载元素中最后一行的元素
this.lastItemIndex = 0; // 保存最后一个元素的下标
this.init();
}
BarrelLayout.prototype.init = function(){
var layoutData = this.calc(this.items);
// 保存最后一行的元素
this.lastRow = layoutData.elemArr[layoutData.elemArr.length - 1];
// 保存最后一个元素的index
this.lastItemIndex = this.items.length;
this.render(layoutData.elemArr, layoutData.rowHeightArr);
}; BarrelLayout.prototype.calc = function(itemsList){
// 私有变量
var resultElemArr = [], // 最终返回的保存每一行的数组
resultRowHeightArr = [], // 最终返回的保存每一行的基本行高的数组
tempElemArr = [], // 保存每一行应有元素的数组
widthRate = 0, // 元素的宽度比例
heightRate = 0, // 元素的高度比例
totalWidth = 0; // 行元素的宽度总和 var len = itemsList.length; for (var i = 0; i < len; i++) {
// 计算元素宽高比例
// 再求出缩放下的宽度
widthRate = itemsList[i].offsetWidth / itemsList[i].offsetHeight;
var curElemWidth = this.baseHeight * widthRate;
totalWidth += curElemWidth; // 如果当元素相加宽度小于容器宽度将它推进 tempElemArr 数组
// totalWidth 加上这个元素的宽度
if(totalWidth <= this.barrelWrap.offsetWidth) {
tempElemArr.push(itemsList[i]); // 如果当前的元素是最后一个且总宽度没有超过容器宽度
// 将此时的tempElemArr 放入 this.rows 数组中 if(i === len - 1) {
resultElemArr.push(tempElemArr);
// 行高设置为默认的baseHeight
resultRowHeightArr.push(this.baseHeight);
} }else {
// 如果当前元素宽度相加大于容器宽度 进行如下操作
// 1.计算当前元素宽度总和与baseHeight 的比率 根据比率设置当前行的高度
// 从而设置行内的每一个元素的高度 调整到最适合的宽度
heightRate = this.baseHeight / (totalWidth - curElemWidth);
// 精确高度到两位小数
var curColHeight = Math.floor(((this.barrelWrap.offsetWidth * heightRate) * 10)) / 10;
// 2.将这一行的行高推入 rowHeight 数组
resultRowHeightArr.push(curColHeight);
// 3.将这一行应有的元素推入
resultElemArr.push(tempElemArr);
// 4.tempElemArr 数组重新填入这个超出容器宽度的元素
tempElemArr = [itemsList[i]];
// 5.重设totalWidth 为这个元素的宽度
totalWidth = curElemWidth if(i === len - 1) {
resultElemArr.push(tempElemArr);
// 行高设置为默认的baseHeight
resultRowHeightArr.push(this.baseHeight);
} }
} return {
elemArr: resultElemArr,
rowHeightArr: resultRowHeightArr
}
}; BarrelLayout.prototype.render = function(rowsArr, rowHeightArr){
var container = document.querySelectorAll('.barrel-container')[0];
if(container === undefined) {
container = document.createElement('ul');
container.className = 'barrel-container';
}else {
// 如果barrel-container 存在证明是刷新操作
// 此时要将视图中容器里面最后一行的li 删掉
// 然后再生成元素 加入到容器中
var rows = container.querySelectorAll('.barrel-row');
container.removeChild(rows[rows.length - 1]);
} for (var i = 0; i < rowsArr.length; i++) {
var li = document.createElement('li');
li.className = 'barrel-row';
for (var k = 0; k < rowsArr[i].length; k++) {
rowsArr[i][k].style.height = rowHeightArr[i] + 'px';
rowsArr[i][k].parentNode.style.display = 'inline-block'; li.appendChild(rowsArr[i][k].parentNode);
container.appendChild(li);
}
} this.barrelWrap.appendChild(container);
}; BarrelLayout.prototype.refresh = function(newItemsList){
// 1. 首先调整最后一行的元素排列
// 对新的元素列表进行切割 分离出新加入的元素 根据this.lastItemIndex进行切割
var newList = Array.prototype.slice.call(newItemsList, this.lastItemIndex),
lastRow = this.lastRow; for (var i = 0; i < lastRow.length; i++) {
lastRow[i].style = '';
}
// 将新加入的元素与上一次渲染后最后一个行的元素列连接起来
var totalList = lastRow.concat(newList);
var layoutData = this.calc(totalList);
this.render(layoutData.elemArr, layoutData.rowHeightArr);
// 对 this.lastItemIndex 重新赋值为下一次刷新做准备
this.lastItemIndex += totalList.length - lastRow.length;
this.lastRow = layoutData.elemArr[layoutData.elemArr.length - 1]; console.log(lastItemIndex);
console.log(lastRow);
};
【js 实践】js 实现木桶布局的更多相关文章
- require.js实践
ASP.NET MVC应用require.js实践 这里有更好的阅读体验和及时的更新:http://pchou.info/javascript/asp.net/2013/11/10/527f6ec41 ...
- ASP.NET MVC应用require.js实践
这里有更好的阅读体验和及时的更新:http://pchou.info/javascript/asp.net/2013/11/10/527f6ec41d6ad.html Require.js是一个支持j ...
- r.js实践
r.js合并实践 项目中用到require.js做生产时模块开发,但上线要合并压缩,幸好它配套有r.js.下面就其用法说明一下. 首先建一个目录,里面的结构如下: require.js可以到r.js项 ...
- 一个简单的 vue.js 实践教程
https://segmentfault.com/a/1190000006776243?utm_source=tuicool&utm_medium=referral 感觉需要改善的地方有: ( ...
- flexible.js + makegrid.js 自适应布局
一,flexible.js 的使用方式: (一),引用方式 1,引用cdn地址 <script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3. ...
- r.js合并实践 --项目中用到require.js做生产时模块开发 r.js build.js配置详解
本文所用源代码已上传,需要的朋友自行下载:点我下载 第一步: 全局安装 npm install -g requirejs 第二步: 1.以下例子主要实现功能, 1)引用jq库获取dom中元素文本, ...
- Rollup.js 实践
音乐分享: B.o.B Ft. Marko Penn - <Roll up> ——————————————————————————————————————————————————————— ...
- 【实践】require.js + r.js 代码打包压缩初体验
第二个分享的是学校项目所接触到的新知识,代码压缩 + 代码打包 这次的项目用了require.js 这个插件做模块化管理的工具,所谓模块化就是在开发的过程中将功能划分成一个独立的模块,使代码可读性更强 ...
- js+canvas实现象棋的布局、走棋位置提示、走棋代码
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
随机推荐
- Head First设计模式之工厂模式
一.定义 定义了一个创建对象的接口, 但由子类决定要实例化的类是哪一个. 工厂方法让类把实例化推迟到子类 二.结构 1.抽象工厂角色:这是工厂方法模式的核心,它与应用程序无关.是具体工厂角色必须实现的 ...
- async await promise
async 异步函数,以后可能会用得很广. 1.箭头函数: 没有{ }时不写return 也有返回值 2.Promise : 异步神器,很多异步api都是基于Promise 3.new Promise ...
- 利用pip批量更新python库
如果python库比较旧,需要更新到最新版本,可以利用pip工具. DOS命令行下,输入pip -V查看pip版本,可以先把pip更新到新版本. 查看系统里过期的python库 pip list #列 ...
- css实现网格背景
只使用一个渐变时,我们能创建的图案并不多,当我们把多个渐变图案组合起来,让他们透过彼此的透明区域显现时,神奇的事情就发生了!我们首先想到的是把水平和水质条纹叠加起来,就可以得到各种各样的网格. 1. ...
- 终端登入mysql
mysql -u 用户名 -p 输入密码 1. 给root用户添加密码,密码为root mysqladmin -u root -p password root 2. 通过终端连接ser ...
- Mysql查询不为null值
Mysql本以为查询不为null就是!=null可是结果查询出来什么都没有,后来才发现不为null应该是is not null ,为null应该是is null.
- KMP算法讲解
老规矩,讲算法前,先说一道小问题吧 给你一个长串和短串,求短串在长串中出现的次数和位置. 设长串长度为len1,短串长度为len2. 如果len1*len2<=108,那就很简单了,直接暴力枚举 ...
- Python模块学习------ 正则表达式
import re #f = open('data.txt','r') #for eachline in f.readlines(): #print re.split('\s\s+', eachlin ...
- python函数,模块及eclipse使用
一.eclipse的使用 1.作用 (1)最好用的IDE (2)可调式debug (3)查看可执行过程 (4)可查看源代码 2.安装eclipse及配置 目录安装Pythonpython for ec ...
- Python如果导出失败,pass函数功能
由于服务器中有一些模块不存在,在文件中导入这些模块时提示错误,导致本地运行正常,服务器测试不通过. 此时,需要捕捉ImportError,当导入的包不存在时,pass掉定义的功能. 示例代码: try ...