Ant Design Table自适应列宽,就是这么简单!

18,973 阅读3分钟

123.jfif

前言

目前在弄一个动态表格的项目,但由于没法知道每个列具体的内容,无法确定表格的列宽,而统一的设定会导致有的列内容很挤,有的列内容很空洞,整体视觉较差。

分析问题

Ant Design的Table组件,column在不设置width的时候,所有列会平分table的宽度,导致有的表头显示有问题,所以我们肯定需要指定每个列的列宽。现在问题是如何精确的算出后台返回字符串长度的像素值(px)

Q: 能不能根据后台返回字符数来确定列宽?
A: 不能。首先我们没法知道后台返回的字符含有的类型,是纯中文还是含有英文,数字,特殊字符等。这会让我们计算不准确

解决问题

本文使用canvas画布来实现对字符串的测量。

前置知识

canvas [传送门]
创建一个canvas对象
document.createElement("canvas")
HTMLCanvasElement.getContext()方法获取这个元素的context——图像稍后将在此被渲染
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}
/>

优点: 每列内容都能一目了然看全
缺点: 每一行由于后台返回内容长度不一样会导致有的行会比较空洞

image.png 适用范围: 内容长度比较均匀的列表。

第二版

  // 定义一个 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}
/>

优点: 每列列宽相对合理,且视觉上比较美观

image.png

总结

总的来说比较符合要求。若表格数据量较大时,此方法计算量也会相应增加,建议数据量大时,可以取前20行数据作为标准计算列宽均值。

如有帮助,请点个赞❤ 谢谢!
完结撒花~~

参考文档

[canvas - MDN]
blog.csdn.net/qiao_qiao_h…