From 3e362241cc6dd1db1d902b7a96e822730dace7d7 Mon Sep 17 00:00:00 2001 From: kron Date: Sun, 16 Nov 2025 17:09:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=88=86=E4=BA=AB=E8=AE=A1?= =?UTF-8?q?=E5=88=86=E8=AF=A6=E6=83=85=E7=9A=84=E9=9D=B6=E5=AD=90=E5=92=8C?= =?UTF-8?q?=E7=82=B9=E7=9A=84=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/point-book-detail.vue | 8 +-- src/util.js | 117 ++++++++++++++++++++++++++++---- 2 files changed, 109 insertions(+), 16 deletions(-) diff --git a/src/pages/point-book-detail.vue b/src/pages/point-book-detail.vue index 2916075..38eac85 100644 --- a/src/pages/point-book-detail.vue +++ b/src/pages/point-book-detail.vue @@ -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)`, }" > - + diff --git a/src/util.js b/src/util.js index 8dd95d6..1e06711 100644 --- a/src/util.js +++ b/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);