设计功能不是很复杂,也不想用插件,最终出现现在版本,主要用到微信小程序 wx.canvasToTempFilePath方法
// 直接调用改方法
createQRCode() {
const qrCodeCanvasId = "qrcodeCanvas";
drawQrcode({
width: 200,
height: 200,
canvasId: "qrcodeCanvas",
text: `https://www.dabanban.cn/face?rotateId=${this.data.rotateId}`,
});
setTimeout(() => {
this.convertCanvasToImage(qrCodeCanvasId);
}, 500);
},
//把 Canvas 转换成图片
convertCanvasToImage(canvasId) {
wx.canvasToTempFilePath({
canvasId: canvasId,
success: (res) => {
// 传给 painter 组件绘制
this.generatePoster(res.tempFilePath);
},
fail: (err) => {
console.error("canvas 转图片失败:", err);
},
});
},
generatePoster(qrCodeUrl) {
const { rankList, maleIcon, femaleIcon, rotateInfo } = this.data;
const bgColors = ["#FFECEC", "#EAF4FF", "#FFF8E1", "#F1F1F1"];
const spacing = 20;
const baseTop = 750;
const defaultAvatar = '/images/default-avatar.png'; // 添加默认头像路径
// 计算每个排名项的基础高度
const getItemHeight = (playerCount) => 80 + (60 * playerCount);
// 计算垂直偏移量
const getVerticalOffset = (index, prevPlayersCount) => {
let totalHeight = 0;
for (let i = 0; i < index; i++) {
const players = Array.isArray(rankList[i].user) ? rankList[i].user : [rankList[i]];
totalHeight += getItemHeight(players.length) + spacing;
}
return baseTop + totalHeight;
};
const views = [
// 标题
{
type: "text",
text: "看看",
css: {
top: "40rpx",
left: "50%",
fontSize: "40rpx",
align: "center",
color: "#333",
fontWeight: "bold",
},
},
// 排行榜背景
{
type: "image",
url: "https://www.dabanban.cn/resource/dabanban/miniImages/rank.png",
css: {
width: "628rpx",
height: "442rpx",
top: "132rpx",
left: "50%",
align: "center",
},
},
// 轮转比赛背景框
{
type: "rect",
css: {
width: "668rpx",
height: "280rpx",
top: "450rpx",
left: "50%",
align: "center",
borderRadius: "20rpx",
color: "#FFFFFF",
borderWidth: "2rpx",
borderColor: "#E0E0E0",
},
},
// 轮转比赛标题
{
type: "text",
text: rotateInfo.title,
css: {
top: "483rpx",
left: "72rpx",
fontSize: "30rpx",
color: "#333",
fontWeight: "bold",
maxLines: 1,
ellipsis: true,
},
},
// 几队
{
type: "text",
text: `${rotateInfo.totalNum}队`,
css: {
top: "547rpx",
left: "72rpx",
fontSize: "26rpx",
color: "#666",
maxLines: 1,
ellipsis: true,
},
},
// 多少人轮转
{
type: "text",
text: `${rotateInfo.rotateTypeName}`,
css: {
top: "547rpx",
left: "120rpx",
fontSize: "26rpx",
color: "#666",
maxLines: 1,
ellipsis: true,
},
},
// 多少分制
{
type: "text",
text: `${rotateInfo.scoreRule}分制`,
css: {
top: "547rpx",
left: "350rpx",
fontSize: "26rpx",
color: "#666",
},
},
// 表头背景
{
type: "rect",
css: {
width: "668rpx",
height: "100rpx",
left: "50%",
top: "650rpx",
align: "center",
borderRadius: "10rpx",
color: "#F0F0F0",
},
},
// 表头文本
{
type: "text",
text: "排名",
css: {
top: "685rpx",
left: `80rpx`,
fontSize: "26rpx",
color: "#666",
fontWeight: "bold",
},
},
{
type: "text",
text: "参赛选手",
css: {
top: "685rpx",
left: `200rpx`,
fontSize: "26rpx",
color: "#666",
fontWeight: "bold",
},
},
{
type: "text",
text: "胜-负",
css: {
top: "685rpx",
left: `485rpx`,
fontSize: "26rpx",
color: "#666",
fontWeight: "bold",
},
},
{
type: "text",
text: "净胜分",
css: {
top: "685rpx",
left: `600rpx`,
fontSize: "26rpx",
color: "#666",
fontWeight: "bold",
},
},
];
// 动态生成选手排名
rankList.forEach((player, index) => {
const players = Array.isArray(player.user) ? player.user : [player];
const playerCount = players.length;
const itemHeight = getItemHeight(playerCount);
const itemTop = getVerticalOffset(index, playerCount);
// 排名项背景
views.push({
type: "rect",
css: {
width: "668rpx",
height: `${itemHeight}rpx`,
left: "50%",
top: `${itemTop}rpx`,
align: "center",
borderRadius: "10rpx",
color: bgColors[index] || "#F1F1F1",
}
});
// 排名数字
views.push({
type: "text",
text: `${index + 1}`,
css: {
top: `${itemTop + itemHeight/2 - 15}rpx`, // 垂直居中
left: "70rpx",
fontSize: "30rpx",
color: "#121212",
fontWeight: "bold",
width: "50rpx",
textAlign: "center",
}
});
// 每个玩家的信息
players.forEach((p, i) => {
const playerTop = itemTop + 25 + (i * 80);
// 头像
views.push({
type: "image",
url: p.imgUrl || defaultAvatar,
css: {
width: "60rpx",
height: "60rpx",
top: `${playerTop}rpx`,
left: "170rpx",
borderRadius: "30rpx",
}
});
// 性别图标
views.push({
type: "image",
url: p.gender === 1 ? femaleIcon : maleIcon,
css: {
width: "30rpx",
height: "30rpx",
top: `${playerTop + 15}rpx`,
left: "240rpx",
}
});
// 玩家昵称
views.push({
type: "text",
text: p.nickName || "匿名玩家",
css: {
top: `${playerTop + 10}rpx`,
left: "280rpx",
fontSize: "28rpx",
color: "#121212",
maxLines: 1,
ellipsis: true,
width: "180rpx",
}
});
});
// 胜负记录和净胜分(显示在第一个玩家行)
views.push(
{
type: "text",
text: `${player.successNum || 0}-${player.failNum || 0}`,
css: {
top: `${itemTop + itemHeight/2 - 15}rpx`,
left: "490rpx",
fontSize: "30rpx",
color: "#121212",
fontWeight: "bold",
}
},
{
type: "text",
text: `${player.score || 0}`,
css: {
top: `${itemTop + itemHeight/2 - 15}rpx`,
left: "590rpx",
fontSize: "30rpx",
color: "#121212",
width: "100rpx",
textAlign: "center",
}
}
);
});
// 计算二维码位置
const qrTop = getVerticalOffset(rankList.length, 0) + 50;
views.push(
{
type: "text",
text: "长按扫码查看详情",
css: {
top: `${qrTop - 30}rpx`,
left: "50%",
fontSize: "22rpx",
color: "#333",
fontWeight: "bold",
align: "center",
},
},
{
type: "image",
url: qrCodeUrl,
css: {
width: "150rpx",
height: "150rpx",
top: `${qrTop + 20}rpx`,
left: "50%",
align: "center",
},
},
{
type: "text",
text: "—0ne 看看—",
css: {
top: `${qrTop + 190}rpx`,
left: "50%",
fontSize: "26rpx",
color: "#333",
fontWeight: "bold",
align: "center",
},
}
);
this.setData({
posterData: {
width: "750rpx",
height: `${qrTop + 280}rpx`,
background: "#F8F9FB",
pixelRatio: 2,
views,
},
});
},
生成后的效果: