fix(share): 二维码不再被静默区吃掉三分之一尺寸;卡内间距收到 4px 栅格
上一版改成按模块整数倍出图时,静默区仍然画在图像里:49 个模块(41 + 4×2) 挤进 165px 只剩 3px/模块、符号 111px,比改造前的 135px 还小 —— 这是自己引入的 回归。测试卡上验证时才发现:二维码在 960 宽的卡里只占 111px,微信/QQ 二次 压缩到 480 宽后模块不足 1.5px,gozxing 直接解不出来。 - generateQR 改 qr.DisableBorder:图像只输出符号,静默区交给承载它的白色卡片 (四周留白 30px 以上,远超 4 模块)。符号尺寸按 size/modules 取整,最多 浪费一个模块宽度 - 调用方按实际符号尺寸居中与排版,不再假设等于目标尺寸 - 单题卡 QQRSize 128 → 150:37 模块的题目链接因此拿到 4px/模块(原 3px) - 卡内垂直间距 26/21/21/15 → 24/20/20/16,落在 4px 栅格上 验证:profile 卡二维码 111px → 148px(37 模块 × 4px,用满 90%),单题卡 111px → 148px;gozxing 解码 960 原图、75% 缩放图均能读出正确 URL(含内嵌 头像)。守门测试加了两条:符号宽度必须等于 modules×perModule 且吃掉目标尺寸 ≥80%(变异成「含静默区」立刻 FAIL),头像测试改判四角必须是定位图案而非 白色(防止符号被裁切)。
This commit is contained in:
+29
-22
@@ -76,7 +76,6 @@ const (
|
||||
// 二维码尺寸
|
||||
const (
|
||||
QRCodeSize = 165
|
||||
QRCodeX = (QRCardW - QRCodeSize) / 2
|
||||
)
|
||||
|
||||
// 社交信息
|
||||
@@ -505,23 +504,25 @@ func ShareCard(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
qrSize := qr.Bounds().Dx()
|
||||
|
||||
const (
|
||||
qrToHintGap = 26
|
||||
qrToHintGap = 24
|
||||
hintLineHeight = 17
|
||||
hintToSepGap = 21
|
||||
hintToSepGap = 20
|
||||
sepHeight = 1
|
||||
sepToLinkGap = 21
|
||||
linkLineHeight = 15
|
||||
sepToLinkGap = 20
|
||||
linkLineHeight = 16
|
||||
)
|
||||
linkLines, linkFontSize := layoutLink(displayURL(boxURL))
|
||||
totalContentH := QRCodeSize + qrToHintGap + hintLineHeight + hintToSepGap + sepHeight + sepToLinkGap + linkLineHeight*len(linkLines)
|
||||
totalContentH := qrSize + qrToHintGap + hintLineHeight + hintToSepGap + sepHeight + sepToLinkGap + linkLineHeight*len(linkLines)
|
||||
startY := QRCardY + (QRCardH-totalContentH)/2
|
||||
|
||||
qrDrawX := QRCardX + QRCodeX
|
||||
qrDrawX := QRCardX + (QRCardW-qrSize)/2
|
||||
qrDrawY := startY
|
||||
draw.Draw(canvas, image.Rect(qrDrawX, qrDrawY, qrDrawX+QRCodeSize, qrDrawY+QRCodeSize), qr, image.Point{}, draw.Over)
|
||||
draw.Draw(canvas, image.Rect(qrDrawX, qrDrawY, qrDrawX+qrSize, qrDrawY+qrSize), qr, image.Point{}, draw.Over)
|
||||
|
||||
hintY := qrDrawY + QRCodeSize + qrToHintGap + hintLineHeight
|
||||
hintY := qrDrawY + qrSize + qrToHintGap + hintLineHeight
|
||||
drawTextCentered(canvas, "扫码向我匿名提问", cardCenterX, hintY, FontSizeQRHint, ColorHintGray, fontCtx)
|
||||
|
||||
sepY := hintY + hintToSepGap
|
||||
@@ -857,24 +858,28 @@ func generateQR(content string, logoImg image.Image, size int) (image.Image, err
|
||||
qr.BackgroundColor = color.RGBA{255, 255, 255, 255}
|
||||
qr.ForegroundColor = color.RGBA{0, 0, 0, 255}
|
||||
|
||||
modules := len(qr.Bitmap()) // 含静默区
|
||||
// 静默区由承载二维码的白色卡片提供(四周留白远超 4 模块),图像本身只输出符号:
|
||||
// 若把静默区画进图里,再叠加居中留白,目标 165px 实际只剩 111px,模块被压到 3px,
|
||||
// 微信/QQ 二次压缩后极易扫不动。
|
||||
qr.DisableBorder = true
|
||||
|
||||
modules := len(qr.Bitmap())
|
||||
perModule := size / modules
|
||||
if perModule < 1 {
|
||||
perModule = 1
|
||||
}
|
||||
symbol := qr.Image(-perModule)
|
||||
|
||||
dst := image.NewRGBA(image.Rect(0, 0, size, size))
|
||||
draw.Draw(dst, dst.Bounds(), &image.Uniform{ColorWhite}, image.Point{}, draw.Src)
|
||||
offset := image.Pt((size-symbol.Bounds().Dx())/2, (size-symbol.Bounds().Dy())/2)
|
||||
draw.Draw(dst, symbol.Bounds().Add(offset), symbol, image.Point{}, draw.Src)
|
||||
dst := image.NewRGBA(symbol.Bounds())
|
||||
draw.Draw(dst, dst.Bounds(), symbol, image.Point{}, draw.Src)
|
||||
|
||||
if logoImg != nil {
|
||||
logoSize := size * 48 / 256
|
||||
w := dst.Bounds().Dx()
|
||||
logoSize := w * 3 / 16
|
||||
logo := imaging.Fill(logoImg, logoSize, logoSize, imaging.Center, imaging.Lanczos)
|
||||
// 白边保证头像与模块之间有对比度,H 级容错(30%)足以覆盖这点面积
|
||||
holder := image.Rect(0, 0, logoSize+4, logoSize+4)
|
||||
holder = holder.Add(image.Pt((size-holder.Dx())/2, (size-holder.Dy())/2))
|
||||
holder = holder.Add(image.Pt((w-logoSize-4)/2, (w-logoSize-4)/2))
|
||||
draw.Draw(dst, holder, &image.Uniform{ColorWhite}, image.Point{}, draw.Src)
|
||||
draw.Draw(dst, logo.Bounds().Add(holder.Min.Add(image.Pt(2, 2))), logo, image.Point{}, draw.Over)
|
||||
}
|
||||
@@ -911,8 +916,7 @@ const (
|
||||
QQRCardH = 255
|
||||
QQRCardX = QContentX + QContentW - QQRCardW
|
||||
QQRCardY = QContentY + (QContentH-QQRCardH)/2
|
||||
QQRSize = 128
|
||||
QQRCodeX = (QQRCardW - QQRSize) / 2
|
||||
QQRSize = 150
|
||||
|
||||
// 内容区最大宽度(左侧,避开二维码卡片)
|
||||
QTextMaxW = QQRCardX - QContentX - 30
|
||||
@@ -1072,12 +1076,15 @@ func QuestionShareCard(ctx context.Context) {
|
||||
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to generate QR code for question share card")
|
||||
return
|
||||
}
|
||||
qrDrawX := QQRCardX + QQRCodeX
|
||||
qrSize := qr.Bounds().Dx()
|
||||
qrDrawX := QQRCardX + (QQRCardW-qrSize)/2
|
||||
// 二维码与提示语整体垂直居中(已删除底部冗余的长链接,卡片更简洁)
|
||||
qrDrawY := QQRCardY + 40
|
||||
draw.Draw(canvas, image.Rect(qrDrawX, qrDrawY, qrDrawX+QQRSize, qrDrawY+QQRSize), qr, image.Point{}, draw.Over)
|
||||
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 + QQRSize + 30
|
||||
hintY := qrDrawY + qrSize + qrToHintGap + hintLineHeight
|
||||
drawTextCentered(canvas, "扫码查看回答", QQRCardX+QQRCardW/2, hintY, 17, ColorHintGray, fontCtx)
|
||||
|
||||
// 底部水印
|
||||
|
||||
Reference in New Issue
Block a user