前言
目前在弄一个动态表格的项目,但由于没法知道每个列具体的内容,无法确定表格的列宽,而统一的设定会导致有的列内容很挤,有的列内容很空洞,整体视觉较差。
分析问题
Ant Design的Table组件,column在不设置width的时候,所有列会平分table的宽度,导致有的表头显示有问题,所以我们肯定需要指定每个列的列宽。现在问题是如何精确的算出后台返回字符串长度的像素值(px)
Q: 能不能根据后台返回字符数来确定列宽?
A: 不能。首先我们没法知道后台返回的字符含有的类型,是纯中文还是含有英文,数字,特殊字符等。这会让我们计算不准确
解决问题
本文使用canvas画布来实现对字符串的测量。
前置知识
canvas [传送门]
创建一个canvas对象
document.createElement("canvas")
方法获取这个元素的context——图像稍后将在此被渲染 HTMLCanvasElement.getContext()
CanvasRenderingContext2D.measureText() 方法返回一个关于被测量文本TextMetrics 对象包含的信息(例如它的宽度TextMetrics.width)。
**注意:** <canvas> 元素本身并没有绘制能力(它仅仅是图形的容器) - 您必须使用脚本来完成实际的绘图任务。
getContext() 方法可返回一个对象,该对象提供了用于在画布上绘图的方法和属性
以下为测量文本实际长度的方法
// 默认字体为微软雅黑 Microsoft YaHei,字体大小为 14px
function getTextWidth(text, font="14px Microsoft YaHei") {
const canvas = document.createElement("canvas");
let context = canvas.getContext("2d");
context.font = font
let textmetrics = context.measureText(text)
return textmetrics.width;
}
自适应列宽表格
第一版
// 定义一个 Map 接收每列的长度值
let widthMap = new Map()
// columns 为动态表格的表头数组 data为展示数据的数组
//作用是遍历所有数据拿到长度最长的一条记下他的宽度
data.forEach(target => {
for(let key in target) {
if(target.hasOwnProperty(key)) {
let keyWidth = getTextWidth(target[key])
let curValue = widthMap.get(key)
// 字段有值就放入数组
widthMap.set(key, Math.max(curValue,keyWidth))
}
}
})
//遍历表头,拿到对应表头的宽度与对应表头下内容比对,取最大值作为列宽,这样可以确保表头不换行。35为表头title左右的padding + border
columns.map((item)=>{
// title,dataIndex为 ant design Table对应参数
let textWidth = getTextWidth(item.title)
if(widthMap.get(item.dataIndex) < textWidth) {
widthMap.set(item.dataIndex, textWidth)
}
return item.width = Math.ceil(widthMap.get(item.dataIndex)) + 35
})
最后组件为
<Table
columns={columns}
dataSource={data}
bordered
rowKey={record => record.id}
/>
优点:
每列内容都能一目了然看全
缺点:
每一行由于后台返回内容长度不一样会导致有的行会比较空洞
适用范围:
内容长度比较均匀的列表。
第二版
// 定义一个 Map 接收每列的长度值
let widthMap = new Map()
// columns 为动态表格的表头数组 data为展示数据的数组
//作用是遍历所有数据拿到长度,记下每一列的宽度
data.forEach(target => {
for(let key in target) {
if(target.hasOwnProperty(key)) {
let keyWidth = getTextWidth(target[key])
// 字段有值就放入数组
widthMap.has(key) ? widthMap.set(key,widthMap.get(key).concat(keyWidth)) : widthMap.set(key,[].concat(keyWidth ? keyWidth : [] ))
}
}
})
// 计算平均值,保证列宽尽量保持均衡
for(let [mapKey] of widthMap) {
let valueArr = widthMap.get(mapKey)
let len = valueArr.length
let value = valueArr.reduce((acc, cur) => acc + 1/cur,0)
widthMap.set(mapKey, len/value)
}
//遍历表头,拿到对应表头的宽度与对应表头下内容比对,取最大值作为列宽,这样可以确保表头不换行。35为表头title左右的padding + border
columns.map((item)=>{
// title,dataIndex为 ant design Table对应参数
let textWidth = getTextWidth(item.title)
if(widthMap.get(item.dataIndex) < textWidth) {
widthMap.set(item.dataIndex, textWidth)
}
return item.width = Math.ceil(widthMap.get(item.dataIndex)) + 35
})
最后组件为
<Table
columns={columns}
dataSource={data}
bordered
rowKey={record => record.id}
/>
优点: 每列列宽相对合理,且视觉上比较美观
总结
总的来说比较符合要求。若表格数据量较大时,此方法计算量也会相应增加,建议数据量大时,可以取前20行数据作为标准计算列宽均值。
如有帮助,请点个赞❤ 谢谢!
完结撒花~~