feat: 优化描述区文字渲染逻辑,动态调整字体大小以适应区域并确保文本垂直居中

This commit is contained in:
xudan 2025-08-12 16:47:28 +08:00
parent 39044f3d65
commit 3e5aad1237

View File

@ -1466,38 +1466,47 @@ function drawArea(ctx: CanvasRenderingContext2D, pen: MapPen): void {
// 如果是描述区且有描述内容,渲染描述文字
if (type === MapAreaType. && desc) {
ctx.fillStyle = get(theme, 'color') ?? '';
// 使用更大的字体显示描述文字
const descFontSize = Math.min(w / 6, h / 1.5, 128); // 根据区域大小自适应字体大小最大128px
// 动态计算字体大小,让文字填充区域
let descFontSize = Math.min(w / 2, h / 2, 200);
let lines: string[] = [];
while (descFontSize > 8) {
ctx.font = `${descFontSize}px ${fontFamily}`;
ctx.textAlign = 'center';
ctx.textBaseline = 'top'; // 使用 top 基准,便于精确计算
const maxCharsPerLine = Math.floor(w / (descFontSize * 0.8));
// 文字换行处理
const words = desc.split('');
const maxCharsPerLine = Math.floor(w / (descFontSize * 0.6)); // 估算每行能容纳的字符数
const lines: string[] = [];
for (let i = 0; i < words.length; i += maxCharsPerLine) {
lines.push(words.slice(i, i + maxCharsPerLine).join(''));
if (maxCharsPerLine < 1) {
descFontSize = Math.floor(descFontSize * 0.9);
continue;
}
// 使用 measureText 精确计算字体高度
const testText = '测试文字'; // 用于测量字体高度的测试文字
const textMetrics = ctx.measureText(testText);
const actualFontHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent;
const lineSpacing = actualFontHeight * 0.2; // 行间距为字体高度的20%
const descLineHeight = actualFontHeight + lineSpacing;
// 文字换行
lines = [];
for (let i = 0; i < desc.length; i += maxCharsPerLine) {
lines.push(desc.slice(i, i + maxCharsPerLine));
}
// 计算整个文本块的总高度
const totalHeight = lines.length * descLineHeight - lineSpacing; // 减去最后一行不需要的行间距
// 计算文本高度
const textMetrics = ctx.measureText('测试文字');
const lineHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent;
const totalHeight = lines.length * lineHeight * 1.1;
// 计算文本块的起始Y坐标确保整个文本块在区域中垂直居中
if (totalHeight <= h * 0.9) break;
descFontSize = Math.floor(descFontSize * 0.9);
}
// 渲染文字
ctx.font = `${descFontSize}px ${fontFamily}`;
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
const textMetrics = ctx.measureText('测试文字');
const lineHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent;
const totalHeight = lines.length * lineHeight * 1.1;
const startY = y + h / 2 - totalHeight / 2;
lines.forEach((line, index) => {
const lineY = startY + index * descLineHeight;
// 水平居中x + w / 2垂直居中lineY
ctx.fillText(line, x + w / 2, lineY);
ctx.fillText(line, x + w / 2, startY + index * lineHeight * 1.1);
});
} else if (type !== MapAreaType. && label) {
// 非描述区才显示标签