效果
1、适合搜素表单布局,查询重置等功能块始终位于最后一行的最后一列
2、适合普通多行两端对齐,未填充满的行左对齐
思路
- 此脚本目的为实现整齐风格的表单布局,为了达到整齐的效果,每个表单元素或者块都要设置一致的 宽度
- flex 布局不能适应多行不同的对齐需求,grid需要额外配置不同分辨率的分割,此脚本只需要设置块宽以及块的默认最小间隔(
块
指效果图中灰色块,脚本中定义为box
) - 通过已知的块宽和最小间隔能够计算出一行最多可容纳多少块,如果宽度富裕则计算出新的间隔
- 浮点运算的不准确,需要计算正数插值,将其平摊到具有右边距 的每个块
代码
此版本为jquery
版,不过只是借用了其事件触发功能用以适应屏幕缩放,其他功能性脚本均为原生js,可以很容易转为其他框架比如vue
需要注意的是,所有的块默认为float:left
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
div {
box-sizing: border-box;
}
.fit-box {
border: 1px solid #aaa;
}
.fit-box:after {
content: '';
display: block;
clear: both
}
.box {
float: left;/*默认左浮动,必须*/
width: 121px;
height: 40px;
margin-top: 10px;
background: #c8c8c8;
/*border: 1px solid #72b48b;*/
}
</style>
</head>
<body>
<!--div.fill-box*5>{$}-->
<div class="fit-box">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
<div class="box">7</div>
<div class="box">8</div>
<div class="box">9</div>
<div class="box">10</div>
<div class="box">11</div>
<div class="box">12</div>
<div class="box">13</div>
<div class="box">14</div>
<div class="box">15</div>
</div>
<script src="../../lib/jquery-1.11.3.min.js"></script>
<script>
;(function () {
function DriftBox($el, options) {
this._$el = $el;
this._options = $.extend({}, options);
}
DriftBox.prototype = {
init() {
let _this = this;
let { _$el, _options } = _this
//初始化时执行一次计算
_this.calculate();
//监听窗口变动
_$el.on('RZ', (e) => {
((fn, params, time) => {
//如果当前存在延时,则清除并重新添加,注意保持method对象的稳定性,不然可能导致tId丢失
clearTimeout(fn.tId);
fn.tId = setTimeout(() => fn.call(_this, params), time)
})(_this.calculate, e, _options.throttleTime)
});
$(window).resize(() => _$el.trigger($.Event('RZ')));
},
/**
* FillBox宽度固定,需要计算每行数量,边距
*
*/
calculate() {
let { searchLayout: isSearchLayout, marginRight: mr, boxSelector } = this._options
let el = this._$el[0]
let boxArr = el.querySelectorAll(boxSelector); //获取所有BOX
let boxNum = boxArr.length;
if (boxNum) {
let rw = el.clientWidth,//行宽
bw = boxArr[0].offsetWidth, //TODO 只考虑BOX同宽,BOX不同宽易导致自动分配的margin过大且需要传送式计算每行BOX数量
num = Math.floor((rw + mr) / (bw + mr)), //每行最多几个BOx
rowNum = Math.ceil(boxNum / num);//分几行
if (num) {
mr = Math.floor((rw - num * bw) / (num - 1));
console.log("num:", num, 'rw:', rw, 'bw:', bw, 'mr:', mr)
//满行最后一个BOX不设置margin-right;
boxArr.forEach((d, i) => d.style.marginRight = (i + 1) % num && mr + 'px');
//如果是类似表单查询的布局,查询和重置|按钮所在box需要右对齐
if (isSearchLayout) {
boxArr[boxNum - 1].style.marginRight = 0
boxArr[boxNum - 1].style.float = 'right'
}
//浮点计算的不准确可能导致每行差几个像素,在视觉上不会太明显,默认将这些插值添加到每行的第一个非右对齐的元素
let dif = rw - num * bw - (num - 1) * mr
//处理每行的差值
let box, marginRight, i, j, m;
for (i = 0; i < rowNum; i++) {
for (j = 0, m = 0; j < dif; m++) {
box = boxArr[i * num + m % num]
marginRight = parseInt(box.style.marginRight)
if (box.style.float == 'right') break
if (marginRight) {
box.style.marginRight = marginRight + 1 + 'px'
j++
}
}
}
console.log('dif:', dif, 'rowNum:', rowNum)
}
/**
* 如果容器高度过大导致外层出现滚动条压缩压缩容器宽度,会使得计算的margin偏小,
* 因此需要在计算完成后重新进行比对,如果容器宽度变化则重新计算一次
*/
requestAnimationFrame(() => rw != el.clientWidth ? this.calculate() : '')
}
}
}
$.fn.DriftBox = function (options) {
this.each(function (index, el) {
new DriftBox($(el), options).init()
});
return $(this)
}
})();
$('.fit-box').DriftBox({
boxSelector: '.box',
searchLayout: false,
throttleTime: 150,
marginRight: 20,//假如指定盒子最小边距为20像素
});
</script>
</body>
</html>