完成分享计分详情的靶子和点的渲染
This commit is contained in:
@@ -92,7 +92,7 @@ const shareImage = async () => {
|
||||
|
||||
onLoad(async (options) => {
|
||||
if (options.id) {
|
||||
const result = await getPointBookDetailAPI(options.id || 222);
|
||||
const result = await getPointBookDetailAPI(options.id || 243);
|
||||
record.value = result;
|
||||
notes.value = result.remark || "";
|
||||
const config = uni.getStorageSync("point-book-config");
|
||||
@@ -281,18 +281,18 @@ onShareTimeline(async () => {
|
||||
class="btns"
|
||||
:style="{
|
||||
gridTemplateColumns: `repeat(${
|
||||
user.id === record.user.id ? 1 : 1
|
||||
user.id === record.user.id ? 2 : 1
|
||||
}, 1fr)`,
|
||||
}"
|
||||
>
|
||||
<button hover-class="none" @click="goBack">关闭</button>
|
||||
<!-- <button
|
||||
<button
|
||||
hover-class="none"
|
||||
@click="shareImage"
|
||||
v-if="user.id === record.user.id"
|
||||
>
|
||||
分享
|
||||
</button> -->
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<ScreenHint2 :show="showTip || showTip2 || showTip3" :onClose="closeTip">
|
||||
|
||||
117
src/util.js
117
src/util.js
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user