perf(share): 单题卡二维码同样提到 5px/模块

单题卡右侧卡片只有 210×255,二维码最大只能到 148px(37 模块 = 4px/模块),
缩到 480 宽照样解不出。

- QQRCardW 210 → 236、QQRCardH 255 → 300、QQRSize 150 → 190(37 模块 → 185px)
  卡片仍居中对齐(x 679、y 120),左侧文字区随 QQRCardX 派生,自动收窄 26px
- 二维码卡片绘制抽成 drawQuestionQRCard,可在测试里直接调用出图
- 新增 TestQuestionQRCardLayout:二维码必须正方形、模块 ≥5px、四边留白 ≥20px、
  居中偏差 ≤1px,TEST_PREVIEW=1 输出整卡 PNG;把 QQRSize 改回 150 立刻 FAIL

实测:二维码 185px(5px/模块)、居中偏差 0.5px、上下留白 34px;
gozxing 在 960 / 720 / 480 / 320 宽下都能解出正确链接。
This commit is contained in:
tamakyi
2026-10-23 00:27:07 +08:00
parent d0a1bb8435
commit 310f85156a
2 changed files with 123 additions and 20 deletions
+27 -20
View File
@@ -913,11 +913,11 @@ const (
QAnswerMaxLine = 7
// 右侧二维码卡片
QQRCardW = 210
QQRCardH = 255
QQRCardW = 236
QQRCardH = 300
QQRCardX = QContentX + QContentW - QQRCardW
QQRCardY = QContentY + (QContentH-QQRCardH)/2
QQRSize = 150
QQRSize = 190
// 内容区最大宽度(左侧,避开二维码卡片)
QTextMaxW = QQRCardX - QContentX - 30
@@ -925,6 +925,29 @@ const (
QWatermarkY = CardHeight - 30
)
// drawQuestionQRCard 绘制单题卡右侧的二维码卡片:二维码与提示语整体垂直居中。
func drawQuestionQRCard(canvas *image.RGBA, user *db.User, question *db.Question, logo image.Image, fontCtx *freetype.Context) error {
cardRect := image.Rect(QQRCardX, QQRCardY, QQRCardX+QQRCardW, QQRCardY+QQRCardH)
drawRoundedRect(canvas, cardRect, color.NRGBA{245, 245, 245, 255}, 20)
drawRoundedRect(canvas, cardRect, ColorWhite, 20)
questionURL := fmt.Sprintf("%s/_/%s/%d", conf.App.ExternalURL, user.Domain, question.ID)
qr, err := generateQR(questionURL, logo, QQRSize)
if err != nil {
return err
}
const qrToHintGap, hintLineHeight = 30, 17
qrSize := qr.Bounds().Dx()
contentH := qrSize + qrToHintGap + hintLineHeight
qrX := QQRCardX + (QQRCardW-qrSize)/2
qrY := QQRCardY + (QQRCardH-contentH)/2
draw.Draw(canvas, image.Rect(qrX, qrY, qrX+qrSize, qrY+qrSize), qr, image.Point{}, draw.Over)
drawTextCentered(canvas, "扫码查看回答", QQRCardX+QQRCardW/2, qrY+qrSize+qrToHintGap+hintLineHeight, FontSizeQRHint, ColorHintGray, fontCtx)
return nil
}
// 单题分享图结果缓存,TTL 30 分钟,上限 16 条(1C1G 内存友好)。
var questionShareCache = newTTLCache(16)
@@ -1066,27 +1089,11 @@ func QuestionShareCard(ctx context.Context) {
_ = textY
// 右侧二维码卡片
cardRect := image.Rect(QQRCardX, QQRCardY, QQRCardX+QQRCardW, QQRCardY+QQRCardH)
drawRoundedRect(canvas, cardRect, color.NRGBA{245, 245, 245, 255}, 20)
drawRoundedRect(canvas, cardRect, ColorWhite, 20)
questionURL := fmt.Sprintf("%s/_/%s/%d", conf.App.ExternalURL, user.Domain, question.ID)
qr, err := generateQR(questionURL, avImg, QQRSize)
if err != nil {
if err := drawQuestionQRCard(canvas, user, question, avImg, fontCtx); err != nil {
http.Error(ctx.ResponseWriter(), "二维码生成失败", http.StatusInternalServerError)
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to generate QR code for question share card")
return
}
qrSize := qr.Bounds().Dx()
qrDrawX := QQRCardX + (QQRCardW-qrSize)/2
// 二维码与提示语整体垂直居中(已删除底部冗余的长链接,卡片更简洁)
const qrToHintGap, hintLineHeight = 30, 17
contentH := qrSize + qrToHintGap + hintLineHeight
qrDrawY := QQRCardY + (QQRCardH-contentH)/2
draw.Draw(canvas, image.Rect(qrDrawX, qrDrawY, qrDrawX+qrSize, qrDrawY+qrSize), qr, image.Point{}, draw.Over)
hintY := qrDrawY + qrSize + qrToHintGap + hintLineHeight
drawTextCentered(canvas, "扫码查看回答", QQRCardX+QQRCardW/2, hintY, 17, ColorHintGray, fontCtx)
// 底部水印
drawTextCentered(canvas, "Powered by 狼的提问箱", CardWidth/2, QWatermarkY, FontSizeFooter, ColorFooterGray, fontCtx)