fix: font

This commit is contained in:
zzc
2026-02-01 16:49:23 +08:00
parent 9dfa72707a
commit 6bc0a2b85f

View File

@@ -863,6 +863,9 @@ const saveByCanvas = async (save = true) => {
}); });
}; };
// 辅助函数rpx 转 px (基于预览容器宽度 506rpx 对应 Canvas 540px)
const r2p = (rpx) => (rpx * 540) / 506;
try { try {
// 1⃣ 画背景 // 1⃣ 画背景
// ⭐ 先加载背景图 // ⭐ 先加载背景图
@@ -874,40 +877,52 @@ const saveByCanvas = async (save = true) => {
ctx.drawImage(bgImg, 0, 0, W, H); ctx.drawImage(bgImg, 0, 0, W, H);
// 2⃣ 半透明遮罩(和你 UI 一致) // 2⃣ 半透明遮罩
ctx.fillStyle = "rgba(0,0,0,0.08)"; ctx.fillStyle = "rgba(0,0,0,0.08)";
ctx.fillRect(0, 0, W, H); ctx.fillRect(0, 0, W, H);
// 3⃣ 标题图片 // 3⃣ 标题图片
if (titleImg) { if (titleImg) {
const titleW = titleImg.width * titleScale.value; const previewBaseWidth = 400; // rpx
const titleH = titleImg.height * titleScale.value; const drawWidth = r2p(previewBaseWidth) * titleScale.value;
const titleX = (W - titleW) / 2 + titleOffsetX.value; const drawHeight = (titleImg.height / titleImg.width) * drawWidth;
const titleY = 40 + titleOffsetY.value; // 预览中是 translate(offsetX, offsetY)top: 40rpx
ctx.drawImage(titleImg, titleX, titleY, titleW, titleH); const titleX = (W - drawWidth) / 2 + r2p(titleOffsetX.value);
const titleY = r2p(40 + titleOffsetY.value);
ctx.drawImage(titleImg, titleX, titleY, drawWidth, drawHeight);
} }
// 4⃣ 祝福语气泡 // 4⃣ 祝福语气泡
// 预览中 .bubble 有 padding: 40rpx且 .card-overlay 有 padding: 30rpx
// 意味着文字距离容器边缘至少有 70rpx
drawBubbleText(ctx, { drawBubbleText(ctx, {
text: targetName.value + "\n " + blessingText.value.content, text: targetName.value + "\n " + blessingText.value.content,
x: 70, x: 0,
y: 260 + bubbleOffsetY.value, y: r2p(230 + bubbleOffsetY.value),
maxWidth: bubbleMaxWidth.value, // 使用动态宽度 maxWidth: r2p(bubbleMaxWidth.value), // 预览中 bubble-text 的宽度
canvasWidth: W, // 传入画布宽度以实现自动居中 canvasWidth: W,
fontSize: fontSize.value, fontSize: r2p(fontSize.value),
lineHeight: fontSize.value * 1.5, lineHeight: r2p(fontSize.value * 1.6), // 预览中是 1.6
backgroundColor: "rgba(255,255,255,0.85)", padding: r2p(40 + 30), // 内部 padding 40 + 容器 padding 30
backgroundColor: "transparent",
textColor: selectedColor.value, textColor: selectedColor.value,
fontFamily: selectedFont.value.family, fontFamily: selectedFont.value.family,
}); });
// 5⃣ 用户信息
// 预览中 user 是 absolute, left: 160 + offsetX, bottom: 40 - offsetY
drawUserBubble(ctx, { drawUserBubble(ctx, {
x: 160 + userOffsetX.value, x: r2p(160 + userOffsetX.value),
y: H - 136 + userOffsetY.value, bottom: r2p(40 - userOffsetY.value),
avatarImg: avatarImg, // 传入 Image 对象 canvasHeight: H,
avatarImg: avatarImg,
username: signatureName.value, username: signatureName.value,
desc: "送上祝福", desc: "送上祝福",
textColor: signatureColor.value, textColor: signatureColor.value,
avatarSize: r2p(64),
padding: r2p(15),
fontSizeName: r2p(24),
fontSizeDesc: r2p(20),
}); });
// 6⃣ 输出 // 6⃣ 输出
@@ -1012,40 +1027,26 @@ function drawBubbleText(ctx, options) {
}); });
// 如果提供了 canvasWidth则自动计算居中的 x 坐标 // 如果提供了 canvasWidth则自动计算居中的 x 坐标
// 预览中文字是左对齐,但整个气泡容器是水平居中的
let drawX = x; let drawX = x;
if (canvasWidth) { if (canvasWidth) {
const totalWidth = maxLineWidth + padding * 2; drawX = (canvasWidth - maxLineWidth) / 2;
drawX = (canvasWidth - totalWidth) / 2;
} }
// 2⃣ 计算气泡尺寸
// const bubbleWidth = maxWidth
// const bubbleHeight = lines.length * lineHeight + padding * 2
// 3⃣ 绘制气泡(圆角矩形)
// drawRoundRect(
// ctx,
// drawX,
// y,
// bubbleWidth,
// bubbleHeight,
// radius,
// backgroundColor
// )
// 4⃣ 绘制文字 // 4⃣ 绘制文字
ctx.fillStyle = textColor; ctx.fillStyle = textColor;
lines.forEach((line, index) => { lines.forEach((line, index) => {
ctx.fillText(line, drawX + padding, y + padding + index * lineHeight); ctx.fillText(line, drawX, y + padding + index * lineHeight);
}); });
} }
function drawUserBubble(ctx, options) { function drawUserBubble(ctx, options) {
const { const {
x = 40, // 气泡起点 x x = 40, // 气泡起点 x
y = 860, // 气泡起点 y y, // 气泡起点 y (如果传了 bottom 则优先计算)
bottom, // 距离底部的距离
canvasHeight,
avatarImg, // CanvasImage 对象 avatarImg, // CanvasImage 对象
username = "zzc", username = "zzc",
desc = "送上祝福", desc = "送上祝福",
@@ -1072,11 +1073,17 @@ function drawUserBubble(ctx, options) {
Math.max(avatarSize, fontSizeName + fontSizeDesc + 4) + padding * 2; Math.max(avatarSize, fontSizeName + fontSizeDesc + 4) + padding * 2;
const bubbleWidth = avatarSize + padding + textWidth + padding * 2; const bubbleWidth = avatarSize + padding + textWidth + padding * 2;
// 计算 y
let drawY = y;
if (typeof bottom !== "undefined" && canvasHeight) {
drawY = canvasHeight - bottom - bubbleHeight;
}
// 1⃣ 绘制气泡(左右半圆) // 1⃣ 绘制气泡(左右半圆)
drawRoundRect( drawRoundRect(
ctx, ctx,
x, x,
y, drawY,
bubbleWidth, bubbleWidth,
bubbleHeight, bubbleHeight,
bubbleHeight / 2, bubbleHeight / 2,
@@ -1085,7 +1092,7 @@ function drawUserBubble(ctx, options) {
// 2⃣ 绘制头像 // 2⃣ 绘制头像
const avatarX = x + padding; const avatarX = x + padding;
const avatarY = y + (bubbleHeight - avatarSize) / 2; const avatarY = drawY + (bubbleHeight - avatarSize) / 2;
ctx.save(); ctx.save();
ctx.beginPath(); ctx.beginPath();
@@ -1104,7 +1111,7 @@ function drawUserBubble(ctx, options) {
// 3⃣ 绘制文字 // 3⃣ 绘制文字
const textX = avatarX + avatarSize + padding; const textX = avatarX + avatarSize + padding;
const textY = y + padding; const textY = drawY + padding;
ctx.fillStyle = textColor; ctx.fillStyle = textColor;
ctx.font = `${fontSizeName}px 'PingFang SC'`; ctx.font = `${fontSizeName}px 'PingFang SC'`;
ctx.fillText(username, textX, textY); ctx.fillText(username, textX, textY);