效果
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._optionslet el = this._$el[0]let boxArr = el.querySelectorAll(boxSelector); //获取所有BOXlet 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)), //每行最多几个BOxrowNum = 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 = 0boxArr[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') breakif (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>