feat: 生成海报

This commit is contained in:
2025-10-03 09:19:05 +08:00
parent 40a043d2a0
commit 5fec10b342
18 changed files with 1032 additions and 200 deletions

View File

@@ -1,4 +1,4 @@
import { useEffect } from "react";
import { useEffect, useImperativeHandle, forwardRef } from "react";
import { Canvas } from "@tarojs/components";
import Taro from "@tarojs/taro";
@@ -11,19 +11,59 @@ function getImageWh(src): Promise<{ width: number; height: number }> {
});
}
const qrCodeUrl =
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/5e013195-fc79-4082-bf06-9aa79aea65ae.png";
const ringUrl =
"http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/b635164f-ecec-434a-a00b-69614a918f2f.png";
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/b635164f-ecec-434a-a00b-69614a918f2f.png";
const Poster = (props) => {
const carouselUrl =
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/63f62c80-ac44-4f3b-bb6c-d7f6e8ebf76d.jpg";
const dateIcon =
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/1b49476e-0eda-42ff-b08c-002ce510df82.jpg";
const avatarUrl =
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/aac792b0-6f81-4192-ae55-04bee417167c.png";
const mapIcon =
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/06b994fa-9227-4708-8555-8a07af8d0c3b.jpg";
// const logo = "http://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/fb732da6-11b9-4022-a524-a377b17635eb.jpg"
const logoText =
"https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/9d8cbc9d-9601-4e2d-ab23-76420a4537d6.png";
const Poster = (props, ref) => {
const { data } = props;
const {
playType,
ntrp,
mainCoursal,
nickname,
avatarUrl,
title,
locationName,
date,
time,
} = data;
useEffect(() => {
drawCard();
}, []);
useImperativeHandle(ref, () => ({
generateImage: () =>
new Promise((resolve, reject) => {
const query = Taro.createSelectorQuery();
query
.select("#cardCanvas")
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node;
// ⚠️ 关键:传 canvas而不是 canvasId
Taro.canvasToTempFilePath({
canvas,
success: (res) => resolve(res.tempFilePath),
fail: (err) => reject(err),
});
});
}),
}));
const drawCard = async () => {
const query = Taro.createSelectorQuery();
query
@@ -47,11 +87,11 @@ const Poster = (props) => {
roundRectGradient(ctx, 0, 0, width, height, 24, "#BFFFEF", "#F2FFFC");
// 顶部图片
const img = await loadImage(canvas, carouselUrl);
const img = await loadImage(canvas, mainCoursal);
// roundRect(ctx, 20, 20, width - 40, width - 40, 20, "#fff");
await drawCoverImage(
ctx,
carouselUrl,
mainCoursal,
img,
10,
10,
@@ -61,8 +101,8 @@ const Poster = (props) => {
);
// 标签
let left = drawTag(ctx, "单打", 18, 18);
drawTag(ctx, "NTRP 2.5 - 3.0", left + 4, 18);
let left = drawTag(ctx, playType, 18, 18);
drawTag(ctx, ntrp, left + 4, 18);
let top = width - 10;
left = 16;
@@ -87,7 +127,7 @@ const Poster = (props) => {
ctx.fillStyle = "#333";
ctx.font = "bold 28px sans-serif";
// ctx.fillText("华巴轮卡 邀你加入球局", 100, 370);
const nickNameText = "华巴轮卡 邀你加入";
const nickNameText = `${nickname} 邀你加入`;
ctx.fillText(nickNameText, left, top);
let textW = ctx.measureText(nickNameText).width;
left += textW;
@@ -102,56 +142,77 @@ const Poster = (props) => {
// 活动标题
ctx.fillStyle = "#333";
ctx.font = "bold 34px sans-serif";
const r = drawTextWrap(
ctx,
"周一晚上浦东新区单打约球",
left,
top,
width - 32,
40
);
let r = drawTextWrap(ctx, title, left, top, width - 32, 40);
top = r.top + 50;
top = r.top + 40;
left = 16;
const dateImg = await loadImage(canvas, dateIcon);
await drawCoverImage(ctx, dateIcon, dateImg, left, top, 40, 40, 8);
left += 40 + 8;
top += 30;
// 时间
ctx.font = "26px sans-serif";
ctx.fillStyle = "#00B578";
const day = "6月20日 (周五)";
ctx.fillText(day, 80, top);
textW = ctx.measureText(day).width;
left = 80 + 8 + textW;
ctx.fillText(date, left, top);
textW = ctx.measureText(date).width;
left += 8 + textW;
ctx.fillStyle = "#333";
ctx.fillText("下午5点 2小时", left, top);
ctx.fillText(time, left, top);
left = 16;
top += 24;
const mapImg = await loadImage(canvas, mapIcon);
await drawCoverImage(ctx, dateIcon, mapImg, left, top, 40, 40, 8);
left += 40 + 8;
top += 30;
// 地址
ctx.fillStyle = "#666";
ctx.font = "26px sans-serif";
drawTextWrap(
ctx,
"因乐驰网球俱乐部 (嘉定江桥万达店)",
80,
560,
480,
34
r = drawTextWrap(ctx, locationName, left, top, width - 32 - left, 34);
left = 16;
top = r.top + 60;
const logoWh = await getImageWh(logoText);
console.log(logoWh);
const logoTextImg = await loadImage(canvas, logoText);
ctx.drawImage(
logoTextImg,
left,
top,
400,
// 56
400 / (logoWh.width / logoWh.height)
);
const qrImg = await loadImage(canvas, qrCodeUrl);
ctx.drawImage(qrImg, width - 12 - 150, top - 50, 160, 160);
left = 16;
top += 400 / (logoWh.width / logoWh.height) + 30;
// 底部文字
ctx.fillStyle = "#333";
ctx.font = "24px sans-serif";
ctx.fillText("有场 · 网球", 40, 960);
ctx.font = "20px sans-serif";
ctx.fillText("长按识别二维码,快来加入,有你就有场!", left, top);
// 小程序码
// const qrcode = await loadImage(canvas, "小程序码路径");
// ctx.drawImage(qrcode, 480, 880, 100, 100);
// 导出图片
Taro.canvasToTempFilePath({
canvas,
success: (res) => {
console.log("导出路径", res.tempFilePath);
},
fail: (err) => console.error(err),
});
// Taro.canvasToTempFilePath({
// canvas,
// success: (res) => {
// console.log("导出路径", res.tempFilePath);
// },
// fail: (err) => console.error(err),
// });
});
};
@@ -291,4 +352,4 @@ const Poster = (props) => {
);
};
export default Poster;
export default forwardRef(Poster);

View File

@@ -1,4 +1,4 @@
@use '~@/scss/themeColor.scss' as theme;
@use "~@/scss/themeColor.scss" as theme;
.upload-source-popup-text {
width: 100%;
@@ -131,14 +131,28 @@
align-items: flex-start;
gap: 8px;
.upload-popup-footer-cancel, .upload-popup-footer-confirm {
font-feature-settings: 'liga' off, 'clig' off;
.upload-popup-footer-cancel,
.upload-popup-footer-confirm {
font-feature-settings: "liga" off, "clig" off;
font-family: "PingFang SC";
box-sizing: border-box;
height: 44px;
border-radius: 12px;
border: 1px solid rgba(0, 0, 0, 0.12);
flex: 1;
display: flex;
justify-content: center;
align-items: center;
color: #000;
font-feature-settings: "liga" off, "clig" off;
font-family: "PingFang SC";
font-size: 14px;
font-style: normal;
font-weight: 600;
line-height: 20px;
letter-spacing: -0.23px;
}
.upload-popup-footer-cancel {
@@ -154,4 +168,4 @@
}
}
}
}
}