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