完成分享计分详情的靶子和点的渲染

This commit is contained in:
kron
2025-11-16 17:09:50 +08:00
parent 8a4b44666f
commit 3e362241cc
2 changed files with 109 additions and 16 deletions

View File

@@ -194,6 +194,98 @@ export function drawRoundedRect(
}
}
export function drawRingCircle(ctx, x, y, text, diameter = 12) {
const fillColor = "#ff4444";
const borderColor = "#ffffff";
const borderWidth = 1;
const r = diameter / 2;
// 传入的 x/y 需要减去半径和边框长度
const cx = x - r - borderWidth;
const cy = y - r - borderWidth;
// 画圆填充
ctx.beginPath();
if (typeof ctx.arc === "function") {
ctx.arc(cx, cy, r, 0, Math.PI * 2);
} else {
// 简易兼容:没有 arc 方法时,用圆角矩形近似
drawRoundedRect(ctx, cx - r, cy - r, r * 2, r * 2, r, fillColor, null, 0);
}
if (typeof ctx.setFillStyle === "function") {
ctx.setFillStyle(fillColor);
} else {
ctx.fillStyle = fillColor;
}
// 添加微弱阴影提升立体感
if (typeof ctx.setShadow === "function") {
ctx.setShadow(0, 1, 2, "rgba(0,0,0,0.25)");
} else {
ctx.shadowColor = "rgba(0,0,0,0.25)";
ctx.shadowBlur = 2;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 1;
}
ctx.fill();
// 重置阴影,避免影响后续描边和文字
if (typeof ctx.setShadow === "function") {
ctx.setShadow(0, 0, 0, "rgba(0,0,0,0)");
} else {
ctx.shadowColor = "transparent";
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
// 画白色 1px 边框
if (typeof ctx.setLineWidth === "function") {
ctx.setLineWidth(borderWidth);
} else {
ctx.lineWidth = borderWidth;
}
if (typeof ctx.setStrokeStyle === "function") {
ctx.setStrokeStyle(borderColor);
} else {
ctx.strokeStyle = borderColor;
}
// 当 arc 不可用时drawRoundedRect 已经完成边框;否则对圆描边
if (typeof ctx.arc === "function") {
ctx.stroke();
}
// 在圆心绘制数字,使用水平缩放实现窄体,并保持圆心居中
const fontSize = 9;
if (typeof ctx.setFontSize === "function") {
ctx.setFontSize(fontSize);
} else {
ctx.font = `${fontSize}px sans-serif`;
}
if (typeof ctx.setFillStyle === "function") {
ctx.setFillStyle("#ffffff");
} else {
ctx.fillStyle = "#ffffff";
}
ctx.save();
ctx.translate(cx, cy);
// 居中对齐
if (typeof ctx.setTextAlign === "function") {
ctx.setTextAlign("center");
} else {
ctx.textAlign = "center";
}
if (typeof ctx.setTextBaseline === "function") {
ctx.setTextBaseline("middle");
} else {
ctx.textBaseline = "middle";
}
ctx.scale(0.7, 1);
ctx.fillText(String(text ?? ""), 0, 0);
ctx.restore();
}
// 将文本按字符拆分并依次向右绘制,每个字符有白色背景,背景宽度根据字符宽度+内边距动态计算
export function drawTextBoxesLine(
ctx,
@@ -887,10 +979,7 @@ export const generateShareImage = async (canvasId, data) => {
try {
const pointBook = uni.getStorageSync("point-book");
const arrowData = data.groups && data.groups[0] ? data.groups[0] : {};
console.log(arrowData);
// const hasPoint = arrowData.list.some((arrow) => arrow.x && arrow.y);
const hasPoint = true;
console.log("generate data:", data, pointBook);
const hasPoint = arrowData.list.some((arrow) => arrow.x && arrow.y);
const ctx = uni.createCanvasContext(canvasId);
const width = 375;
@@ -906,8 +995,8 @@ export const generateShareImage = async (canvasId, data) => {
const avatarUrl = await loadImage(data.user.avatar);
drawRoundImage(ctx, avatarUrl, 13, 13, 54, 54, 27, "#000", 1);
renderText(ctx, data.user.name, 18, "#000", 84, 36);
renderText(ctx, data.recordDate, 9, "#fff", 350, 24, "center", 38);
renderText(ctx, "今日打卡", 13, "#fff", 338, 36, "center", 38);
renderText(ctx, data.recordDate, 9, "#fff", 350, 24, "center", 39);
renderText(ctx, "今日打卡", 13, "#fff", 336, 38, "center", 39);
// 以第一个字的 x 为起点,右侧字符依次向右排列,每个字符有白色背景并留 5 像素内边距
let cursorX = 84;
cursorX = drawTextBoxesLine(
@@ -976,7 +1065,11 @@ export const generateShareImage = async (canvasId, data) => {
renderText(ctx, "落点分布", 13, "#999", 32, 242);
const bowUrl = await loadImage(pointBook.bowtargetType.iconPng);
ctx.drawImage(bowUrl, 375 * 0.08, 250, 375 * 0.84, 375 * 0.84);
arrowData.list.forEach((arrow) => {});
arrowData.list.forEach((arrow, index) => {
const px = 375 * 0.08 + 375 * 0.84 * arrow.x;
const py = 250 + 375 * 0.84 * arrow.y;
drawRingCircle(ctx, px, py, index + 1);
});
}
const ringNames = ["X", 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, "M"];
const ringBarHeight = hasPoint ? 700 : 340;
@@ -1012,7 +1105,7 @@ export const generateShareImage = async (canvasId, data) => {
9,
"#333",
startX + index * (barWidth + barSpacing),
startY - barHeight - 3
startY - barHeight - (hasPoint ? 6 : 3)
);
}
ctx.fillStyle = barColor(ring.rate);
@@ -1040,17 +1133,17 @@ export const generateShareImage = async (canvasId, data) => {
const pointBookQrcodeUrl = await loadImage(
"https://static.shelingxingqiu.com/attachment/2025-11-13/de7fzgghsfgqu0ytu6.png"
);
ctx.drawImage(pointBookQrcodeUrl, 40, hasPoint ? 715 : 360, 68, 68);
renderText(ctx, "射灵星球", 18, "#333", 120, hasPoint ? 737 : 380);
ctx.drawImage(pointBookQrcodeUrl, 40, hasPoint ? 715 : 358, 68, 68);
renderText(ctx, "射灵星球", 18, "#333", 120, hasPoint ? 734 : 380);
renderText(
ctx,
"高效记录每一箭,快来一起打卡吧!",
13,
"#999",
120,
hasPoint ? 759 : 402
hasPoint ? 756 : 402
);
renderText(ctx, "扫码打卡", 13, "#FFA118", 120, hasPoint ? 780 : 422);
renderText(ctx, "扫码打卡", 13, "#FFA118", 120, hasPoint ? 777 : 422);
ctx.draw();
} catch (e) {
console.error("generateShareImage 绘制失败:", e);