ant-design-vue自动计算a-table每一列的宽度的实现

基于 Canvas 的ant-design-vue的表格列宽自动计算方案

声明: 本文章借鉴了这位朋友的算法,对进行了优化和改正。Ant Design Table自适应列宽,就是这么简单!前言 目前在弄一个动态表格的项目,但由于没法知道每个列具体的内 - 掘金

前言

在开发后台管理系统时,表格是最常用的组件之一。Ant Design Vue 的 Table 组件虽然功能强大,但在列宽自适应方面存在一些不足。本文将介绍一种基于 Canvas 的表格列宽自动计算方案,帮助开发者摆脱手动写width的烦恼。

问题分析

在使用 Ant Design Vue 的 Table 组件时,我们经常会遇到以下问题:

  1. 列宽设置不合理导致内容显示不完整
  2. 手动设置列宽需要反复调整
  3. 不同数据量下同一列的宽度需求不同
  4. 表头文字和内容文字长度不一致

解决方案

核心思路是:

  1. 使用 Canvas 测量文本实际宽度
  2. 遍历数据计算每列最大宽度
  3. 考虑表头文字宽度
  4. 添加适当的内边距

核心代码实现

typescript 复制代码
import type { ColumnType } from "ant-design-vue/es/table";

// 创建 Canvas 上下文
// 使用立即执行函数,避免每一次都重新创建canvas节点以增加使用成本
let context: CanvasRenderingContext2D | null = null;
(() => {
  const canvas = document.createElement("canvas");
  context = canvas.getContext("2d");
})();

// 获取文本宽度
function getTextWidth(text: string, font = "14px Microsoft YaHei") {
  if (!context) return 0;
  context.font = font;
  const textmetrics = context.measureText(text);
  return textmetrics.width;
}

/**
 * 计算表格列宽
 * @param columns 表格表头
 * @param data 表格数据
 * @param options 配置项
 * @returns 计算后的列宽
 */
const calculateColumnWidth = (
  columns: ColumnType[], 
  data: any[],
  options: {
    padding: number;
    font: string;
  }
) => {
  // 存储每列的宽度值
  let widthMap = new Map();
  
  // 遍历数据计算内容宽度
  data.forEach((target) => {
    for (let key in target) {
      // 参考ant-design-vue的官方文档,要考虑dataIndex的两种情况.
      // 1. dataIndex是string
      // 2. dataIndex是一个数组
      const column = columns.find((item) => {
        if (typeof item.dataIndex === "string") {
          return item.dataIndex === key;
        } else if (isArray(item.dataIndex)) {
           // 这里直接取列表中的第一个值进行匹配就好
          return item.dataIndex[0] === key;
        }
      });
      // 优化:如果column中设置了width,则不计算宽度
      if (column?.width) {
        continue;
      }
      let text = target[key];
      // 处理自定义渲染逻辑
      if (column?.customRender) {
        text = column.customRender({
          text,
          value: target[key],
          record: target,
          column,
        });
        if (typeof text === "string") {
          text = text.trim();
        } else if (typeof text === "object") {
          // 如果拿到的不是一个string,那么是使用了h函数将其渲染成了dom
          // 不需要计算宽度
          continue;
        }
      }
      
      // 计算文本宽度
      let keyWidth = getTextWidth(text);
      widthMap.has(key)
        ? widthMap.set(key, widthMap.get(key).concat([keyWidth]))
        : widthMap.set(key, [keyWidth]);
    }
  });

  // 计算平均值
  for (let [mapKey] of widthMap) {
    let valueArr = widthMap.get(mapKey);
    let len = valueArr.length;
    let value = valueArr.reduce((acc: number, cur: number) => acc + 1 / cur, 0);
    widthMap.set(mapKey, len / value);
  }

  // 返回处理后的列配置
  return columns.map((item) => {
     // 如果已经设置了宽度,那么这里就不再设置
    if (item.width) return { ...item, width: item.width };
    
    let textWidth = getTextWidth(item.title as string);
    let index = isArray(item.dataIndex) ? item.dataIndex[0] : item.dataIndex;
    
    if (widthMap.get(index) < textWidth) {
      widthMap.set(index, textWidth);
    }
    
    return {
      ...item,
      width: Math.ceil(widthMap.get(index)) + options.padding,
    };
  });
};

使用方式

typescript 复制代码
const computedColumns = computed(() => {
    calculateColumnWidth(yourColumns, yourData, yourOptions)
})

技术要点

  1. Canvas 测量文本宽度

    • 使用 measureText 方法获取文本实际宽度
    • 支持自定义字体和大小
  2. 数据处理

    • 处理数组类型的 dataIndex
    • 支持自定义渲染函数
    • 考虑表头文字长度
  3. 性能优化

    • 使用 Map 存储中间计算结果
    • 采用平均值计算方式平衡列宽
  4. 可配置性

    • 支持自定义内边距
    • 支持自定义字体
    • 保留手动设置列宽的能力

注意事项

  1. 对于复杂渲染的内容(如自定义组件),建议手动设置列宽
  2. 大量数据时注意性能影响
  3. 不同浏览器下字体渲染可能有差异
  4. 需要考虑表格容器宽度限制

总结

通过这种方案,我们可以实现表格列宽的智能计算,提升用户体验和开发体验。 希望这篇文章能帮助到有类似需求的开发者。如果你有任何问题或建议,欢迎在评论区留言讨论。

相关推荐
RadiumAg1 小时前
记一道有趣的面试题
前端·javascript
yangzhi_emo1 小时前
ES6笔记2
开发语言·前端·javascript
yanlele1 小时前
我用爬虫抓取了 25 年 5 月掘金热门面试文章
前端·javascript·面试
中微子2 小时前
React状态管理最佳实践
前端
烛阴2 小时前
void 0 的奥秘:解锁 JavaScript 中 undefined 的正确打开方式
前端·javascript
中微子3 小时前
JavaScript 事件与 React 合成事件完全指南:从入门到精通
前端
Hexene...3 小时前
【前端Vue】如何实现echarts图表根据父元素宽度自适应大小
前端·vue.js·echarts
初遇你时动了情3 小时前
腾讯地图 vue3 使用 封装 地图组件
javascript·vue.js·腾讯地图
华子w9089258593 小时前
基于 SpringBoot+VueJS 的农产品研究报告管理系统设计与实现
vue.js·spring boot·后端
天天扭码3 小时前
《很全面的前端面试题》——HTML篇
前端·面试·html