fix: font
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user