基于 Canvas 的ant-design-vue的表格列宽自动计算方案
声明: 本文章借鉴了这位朋友的算法,对进行了优化和改正。Ant Design Table自适应列宽,就是这么简单!前言 目前在弄一个动态表格的项目,但由于没法知道每个列具体的内 - 掘金
前言
在开发后台管理系统时,表格是最常用的组件之一。Ant Design Vue 的 Table 组件虽然功能强大,但在列宽自适应方面存在一些不足。本文将介绍一种基于 Canvas 的表格列宽自动计算方案,帮助开发者摆脱手动写width的烦恼。
问题分析
在使用 Ant Design Vue 的 Table 组件时,我们经常会遇到以下问题:
- 列宽设置不合理导致内容显示不完整
- 手动设置列宽需要反复调整
- 不同数据量下同一列的宽度需求不同
- 表头文字和内容文字长度不一致
解决方案
核心思路是:
- 使用 Canvas 测量文本实际宽度
- 遍历数据计算每列最大宽度
- 考虑表头文字宽度
- 添加适当的内边距
核心代码实现
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)
})
技术要点
-
Canvas 测量文本宽度
- 使用
measureText
方法获取文本实际宽度 - 支持自定义字体和大小
- 使用
-
数据处理
- 处理数组类型的 dataIndex
- 支持自定义渲染函数
- 考虑表头文字长度
-
性能优化
- 使用 Map 存储中间计算结果
- 采用平均值计算方式平衡列宽
-
可配置性
- 支持自定义内边距
- 支持自定义字体
- 保留手动设置列宽的能力
注意事项
- 对于复杂渲染的内容(如自定义组件),建议手动设置列宽
- 大量数据时注意性能影响
- 不同浏览器下字体渲染可能有差异
- 需要考虑表格容器宽度限制
总结
通过这种方案,我们可以实现表格列宽的智能计算,提升用户体验和开发体验。 希望这篇文章能帮助到有类似需求的开发者。如果你有任何问题或建议,欢迎在评论区留言讨论。