linux终端如何分栏,动态分栏布局实现

最近项目上要在浏览器端实现一个类似 Linux 下 ls 命令的显示效果。虽然实现过程没花多长时间,但是觉得这个效果还挺赞的,所以分享一下。

神奇的ls命令

ls命令对于前端同学可能比较陌生,简单来说就是Linux终端中的一个常用功能,用来显示某个目录下的文件(夹)列表。不过这它的功能不是本文讨论的重点。重点是它神奇的显示效果,比如下面这样:

AAffA0nNPuCLAAAAAElFTkSuQmCC

看似并无神奇之处,就是文件(夹)名称逐行显示而已,别急,再来看下面两张截图:

AAffA0nNPuCLAAAAAElFTkSuQmCC

AAffA0nNPuCLAAAAAElFTkSuQmCC

不知道你现在有没有发现布局发生了变化:由最初的1列变成了多列!

你很容易想到列数量会随着窗口的宽度而改变,这种猜测是对的,但并不全面,因为决定其显示排列的还有文件(夹)名本身的长度。比如我再指定目录执行一条 ls 命令进行对比:

AAffA0nNPuCLAAAAAElFTkSuQmCC

两条命令执行时窗口宽度是一样的,但是在长文件(夹)名的情况下是3列,而短文件(夹)下是4列。

ok~了解完ls命令的显示特点之后问题来了,我们该怎么实现呢?

实现难点与解决方案

更多文章请关注公众号“Web学习社”。

这种列数发生变化的情况在网站页面中并不少见,你可能第一个就会想到媒体查询或者Bootstrap。但很遗憾事情并没有这么简单。媒体查询和Bootstrap都是针对不同屏幕宽度进行的自适应,而并不会根据内容自动调整,无法满足我们的要求。

似乎浮动可以根据内容大小来进行自动排列,但和我们的需求相比也有一个很大的差别。那就是ls命令在调整好合适的列数之后,每列文件(夹)名都是等宽左对齐的。而使用浮动后的效果是每行长的名称排列少,短的名称排列多,造成列数不统一,参差不齐。

到此看似毫无头绪,不过可以参考“把大象放进冰箱里”分几步的例子,分步骤来解决这个问题。这个问题我们其实可以分两步进行分析:

显示的列数需要根据窗口大小和名称长度两者共同决定,按照已知的使用css的方式无法解决。那么我们只能用最笨的方式进行动态计算。

列数确定以后要确保每列等宽等间距,且左对齐,这个比较容易实现,可以设置等宽等间距。

按照这个思路我们来实现第1步,那就是怎么计算出合适的列数。看看列数是由哪些因素决定的:

窗口的宽度。

名称的长度。

名称之间的横向间距。

窗口的宽度可以通过dom获取或者样式设定,名称宽度可以通过fontSize进行计算,一般一个中文字符宽度为一个fontSize值,而英文数字则为一半,这里我们为了方便计算,只考虑英文和数字的情况。横向间距则可以由样式设定。那么可以得出计算公式:

窗口宽度 >= 列数 * (名称长度 * fontSize/2 + 间距)

这个不等式并不准确,因为当列数等于1的时候是最有可能满足这个不等式的,但显然不是我们想要的结果,所以还要加上另一个不等式:

窗口宽度 < (列数 + 1) * (名称长度 * fontSize/2 + 间距)

满足这两个条件的列数才是我们的最优解。不等式右边的表达式实际上就是行宽,更准确的说是每一行的行宽。当然这个表达式其实也不严谨,更严谨的是下面这样:

窗口宽度 >= (名称长度1 * fontSize/2 + 间距) + ... + (名称长度n * fontSize/2 + 间距)

窗口宽度 < (名称长度1 * fontSize/2 + 间距) + ... + (名称长度n+1 * fontSize/2 + 间距)

代码如下:

/ **

* 根据列数和名称长度进行分列,以二维数组的方式返回结果,如果超出宽度则返回空数组

* list {Array} 待分列的名称数组

* colnum {Number} 列数

* size {Number} 字体大小

* width {Number} 用父容器的宽度替代前面所说的窗口宽度

* spaceWidth {Number} 间距

*/

var subfield = function(list, colnum, size, width, spaceWidth) {

// 使用lodash的chunk函数将数组按照列数分割成二维数组

var result = _.chunk(list, Math.min(colnum, list.length))

var rowWidth = 0

// 逐列计算宽度

for(var col=0;col

colWidth = 0

for(var row=0; row

var item = result[row][col] || ''

// 每列宽度取决于最长的名称宽度

colWidth = Math.max(colWidth, item.length * size / 2 + spaceWidth)

}

rowWidth += colWidth

}

return rowWidth < width ? result : []

}

有了上面的不等式,那么现在我们就可以开始逐行计算了,不过我们从分几列开始算起?如果从分1列的方式算起,那么大多数情况下前面的分列方式都不是最优解,所以我们考虑直接从最优解算起,找到最长的名称和最短的名称并假设它们在一列,如果不满足则减少一列。

// 先取最理想的列数

var colnum = Math.max(1, Math.floor((containerWidth - maxFile.length * fontSize / 2 - spaceWidth) / (spaceWidth + fontSize / 2 * minFile.length)))

var result = []

// 按照递减方式直到找到最优解

while (colnum > 0 && result.length===0) {

if (colnum === 1) {

result = []

for(var i=files.length;i>0;i--) {

result.push([files.shift()])

}

} else {

result = subfield(files, colnum, fontSize, containerWidth, spaceWidth)

}

colnum--

}

至此,我们就完成了第1步。第2步这种布局方式其实很常见,其实就是古老的table布局,那么我们渲染到页面的时候直接使用table来实现。

var render = function(list) {

var str = ''

list.forEach(function(row) {

str += '

'+ row.join('') + ''

})

document.querySelector('.ls table').innerHTML = str

}

具体代码:

AAffA0nNPuCLAAAAAElFTkSuQmCC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值