fix: card content font

This commit is contained in:
zzc
2026-01-22 11:04:47 +08:00
parent bdaf3a3be1
commit 0181066b34

View File

@@ -228,7 +228,8 @@
</view> </view>
<canvas <canvas
canvas-id="cardCanvas" type="2d"
id="cardCanvas"
class="hidden-canvas" class="hidden-canvas"
style="width: 540px; height: 960px" style="width: 540px; height: 960px"
/> />
@@ -275,12 +276,17 @@ const fontList = [
{ {
name: "手写", name: "手写",
family: "ShouXie", family: "ShouXie",
url: "https://file.lihailezzc.com/resource/font/shouxie.ttf", // 示例地址 url: "https://file.lihailezzc.com/ZhiMangXing-Regular.ttf", // 示例地址
}, },
{ {
name: "可爱", name: "可爱",
family: "KeAi", family: "KeAi",
url: "https://file.lihailezzc.com/resource/font/keai.ttf", // 示例地址 url: "https://file.lihailezzc.com/ZCOOLKuaiLe-Regular.ttf", // 示例地址
},
{
name: "草书",
family: "LiuJianMaoCao",
url: "https://file.lihailezzc.com/LiuJianMaoCao-Regular.ttf", // 示例地址
}, },
]; ];
const selectedFont = ref(fontList[0]); const selectedFont = ref(fontList[0]);
@@ -289,8 +295,10 @@ const changeFont = (font) => {
if (font.url) { if (font.url) {
uni.showLoading({ title: "加载字体中" }); uni.showLoading({ title: "加载字体中" });
uni.loadFontFace({ uni.loadFontFace({
global: true,
family: font.family, family: font.family,
source: `url("${font.url}")`, source: `url("${font.url}")`,
scopes: ["webview", "native"],
success: () => { success: () => {
selectedFont.value = font; selectedFont.value = font;
uni.hideLoading(); uni.hideLoading();
@@ -496,75 +504,113 @@ const showMore = () => {
}; };
const saveByCanvas = async (save = true) => { const saveByCanvas = async (save = true) => {
const ctx = uni.createCanvasContext("cardCanvas"); return new Promise((resolve, reject) => {
const query = uni.createSelectorQuery();
query
.select("#cardCanvas")
.fields({ node: true, size: true })
.exec(async (res) => {
if (!res[0] || !res[0].node) {
reject("Canvas not found");
return;
}
// 画布尺寸rpx 转 px const canvas = res[0].node;
const W = 540; const ctx = canvas.getContext("2d");
const H = 960;
// 1⃣ 画背景 // 初始化画布尺寸
// ⭐ 先加载背景图 const dpr = uni.getSystemInfoSync().pixelRatio;
const [bgPath, avatarPath] = await Promise.all([ // 保持 540x960 的逻辑尺寸,为了清晰度可以考虑 * dpr
loadImage(currentTemplate?.value?.imageUrl), // 但为了保持和原来一致的输出尺寸,这里先固定物理尺寸
loadImage(userAvatar.value), // 如果要高清,可以 set width = 540 * dpr然后 scale(dpr, dpr)
]); // 这里为了简单兼容原逻辑,我们让物理尺寸等于逻辑尺寸
canvas.width = 540;
canvas.height = 960;
ctx.drawImage(bgPath, 0, 0, W, H); // 画布尺寸rpx 转 px
const W = 540;
const H = 960;
// 2⃣ 半透明遮罩(和你 UI 一致) // 辅助函数:加载图片为 Image 对象
ctx.setFillStyle("rgba(0,0,0,0.08)"); const loadCanvasImage = (url) => {
ctx.fillRect(0, 0, W, H); return new Promise((resolve, reject) => {
const img = canvas.createImage();
img.onload = () => resolve(img);
img.onerror = (e) => reject(e);
img.src = url;
});
};
// 3⃣ 标题 try {
ctx.setFillStyle("#ffffff"); // 1⃣ 画背景
ctx.setFontSize(42); // ⭐ 先加载背景图
ctx.setTextAlign("center"); const [bgImg, avatarImg] = await Promise.all([
ctx.fillText("新春快乐", W / 2, 120); loadCanvasImage(currentTemplate?.value?.imageUrl),
loadCanvasImage(userAvatar.value),
]);
ctx.setFontSize(22); ctx.drawImage(bgImg, 0, 0, W, H);
ctx.setGlobalAlpha(0.9);
ctx.fillText("2026 YEAR OF THE HORSE", W / 2, 165);
ctx.setGlobalAlpha(1);
// 4️⃣ 祝福语气泡 // 2️⃣ 半透明遮罩(和你 UI 一致)
drawBubbleText(ctx, { ctx.fillStyle = "rgba(0,0,0,0.08)";
text: targetName.value + "\n " + blessingText.value, ctx.fillRect(0, 0, W, H);
x: 70,
y: 260 + bubbleOffsetY.value,
maxWidth: 400,
fontSize: 32,
lineHeight: 46,
backgroundColor: "rgba(255,255,255,0.85)",
textColor: selectedColor.value,
fontFamily: selectedFont.value.family,
});
drawUserBubble(ctx, { // 3⃣ 标题
x: 40 + userOffsetX.value, ctx.fillStyle = "#ffffff";
y: H - 120, ctx.font = "42px sans-serif"; // 默认字体
avatarPath: avatarPath, ctx.textAlign = "center";
username: signatureName.value, ctx.textBaseline = "alphabetic"; // Canvas 2D 默认是 alphabetic
desc: "送上祝福", ctx.fillText("新春快乐", W / 2, 120);
});
// 6⃣ 输出 ctx.font = "22px sans-serif";
const tempPath = await new Promise((resolve, reject) => { ctx.globalAlpha = 0.9;
ctx.draw(false, () => { ctx.fillText("2026 YEAR OF THE HORSE", W / 2, 165);
uni.canvasToTempFilePath({ ctx.globalAlpha = 1;
canvasId: "cardCanvas",
success: (res) => { // 4⃣ 祝福语气泡
if (save) saveImage(res.tempFilePath); drawBubbleText(ctx, {
resolve(res.tempFilePath); text: targetName.value + "\n " + blessingText.value,
}, x: 70,
fail: (err) => reject(err), y: 260 + bubbleOffsetY.value,
maxWidth: 400,
fontSize: 32,
lineHeight: 46,
backgroundColor: "rgba(255,255,255,0.85)",
textColor: selectedColor.value,
fontFamily: selectedFont.value.family,
});
drawUserBubble(ctx, {
x: 160 + userOffsetX.value,
y: H - 120,
avatarImg: avatarImg, // 传入 Image 对象
username: signatureName.value,
desc: "送上祝福",
});
// 6⃣ 输出
uni.canvasToTempFilePath({
canvas: canvas, // Canvas 2D 必须传 canvas 实例
width: W,
height: H,
destWidth: W,
destHeight: H,
success: (res) => {
if (save) saveImage(res.tempFilePath);
resolve(res.tempFilePath);
},
fail: (err) => reject(err),
});
} catch (error) {
console.error("Canvas draw error:", error);
reject(error);
}
}); });
});
}); });
return tempPath;
}; };
const loadImage = (url) => { const loadImage = (url) => {
// 此函数保留给其他可能用到的地方,但 saveByCanvas 内部使用 loadCanvasImage
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
uni.getImageInfo({ uni.getImageInfo({
src: url, src: url,
@@ -607,12 +653,11 @@ function drawBubbleText(ctx, options) {
fontSize = 32, fontSize = 32,
fontFamily = "PingFang SC", fontFamily = "PingFang SC",
} = options; } = options;
console.log(111111, fontFamily);
if (!text) return; if (!text) return;
ctx.setFontSize(fontSize); ctx.fillStyle = textColor;
ctx.setFillStyle(textColor); ctx.font = `${fontSize}px '${fontFamily}'`;
ctx.font = `${fontSize}px ${fontFamily}`;
ctx.textAlign = "left"; ctx.textAlign = "left";
ctx.textBaseline = "top"; ctx.textBaseline = "top";
@@ -654,7 +699,7 @@ function drawBubbleText(ctx, options) {
// ) // )
// 4⃣ 绘制文字 // 4⃣ 绘制文字
ctx.setFillStyle(textColor); ctx.fillStyle = textColor;
lines.forEach((line, index) => { lines.forEach((line, index) => {
ctx.fillText(line, x + padding, y + padding + index * lineHeight); ctx.fillText(line, x + padding, y + padding + index * lineHeight);
@@ -665,7 +710,7 @@ function drawUserBubble(ctx, options) {
const { const {
x = 40, // 气泡起点 x x = 40, // 气泡起点 x
y = 860, // 气泡起点 y y = 860, // 气泡起点 y
avatarPath, // 头像本地路径 avatarImg, // CanvasImage 对象
username = "zzc", username = "zzc",
desc = "送上祝福", desc = "送上祝福",
avatarSize = 64, // 头像直径 avatarSize = 64, // 头像直径
@@ -678,11 +723,11 @@ function drawUserBubble(ctx, options) {
// 设置字体 // 设置字体
ctx.textBaseline = "top"; ctx.textBaseline = "top";
ctx.font = `${fontSizeName}px PingFang SC`; ctx.font = `${fontSizeName}px 'PingFang SC'`;
// 测量文字宽度 // 测量文字宽度
const nameWidth = ctx.measureText(username).width; const nameWidth = ctx.measureText(username).width;
ctx.font = `${fontSizeDesc}px PingFang SC`; ctx.font = `${fontSizeDesc}px 'PingFang SC'`;
const descWidth = ctx.measureText(desc).width; const descWidth = ctx.measureText(desc).width;
// 计算气泡宽度和高度 // 计算气泡宽度和高度
@@ -716,19 +761,21 @@ function drawUserBubble(ctx, options) {
Math.PI * 2, Math.PI * 2,
); );
ctx.clip(); ctx.clip();
ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize); if (avatarImg) {
ctx.drawImage(avatarImg, avatarX, avatarY, avatarSize, avatarSize);
}
ctx.restore(); ctx.restore();
// 3⃣ 绘制文字 // 3⃣ 绘制文字
const textX = avatarX + avatarSize + padding; const textX = avatarX + avatarSize + padding;
const textY = y + padding; const textY = y + padding;
ctx.setFillStyle(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);
ctx.font = `${fontSizeDesc}px PingFang SC`; ctx.font = `${fontSizeDesc}px 'PingFang SC'`;
ctx.setGlobalAlpha(0.6); ctx.globalAlpha = 0.6;
ctx.fillText(desc, textX, textY + fontSizeName + 4); ctx.fillText(desc, textX, textY + fontSizeName + 4);
ctx.setGlobalAlpha(1); ctx.globalAlpha = 1;
} }
function drawRoundRect(ctx, x, y, w, h, r, color) { function drawRoundRect(ctx, x, y, w, h, r, color) {