fix(share): 分享海报大数字压状态文案+删二维码与顶部品牌名
- 根因:状态文案 y=172(22px)、大数字基线 y=230(最大90px),多位数 分钟数字顶部≈165 落入文案区(150~172),压住"完成"文案 - 重排垂直节奏:卡片放大,状态文案 y=184,大数字基线 y=286(上限84px, 与文案下沿间距 34 安全),连续打卡 y=332 - 删顶部"平板的支撑"品牌名、删右下角二维码及"扫码一起打卡"引导 - 删除废弃资源 static/share-qr.jpg;导出改为绘制完直接导出无需等图
This commit is contained in:
@@ -15,10 +15,16 @@
|
|||||||
## 新增:训练完成分享海报(canvas 绘制 + 小程序码)
|
## 新增:训练完成分享海报(canvas 绘制 + 小程序码)
|
||||||
- **现象**:训练完成后点分享,微信默认截取当前页面,因完成弹窗垂直居中,卡片只截到上半部分(绿色星星+"完成!"),大数字和按钮被切掉。
|
- **现象**:训练完成后点分享,微信默认截取当前页面,因完成弹窗垂直居中,卡片只截到上半部分(绿色星星+"完成!"),大数字和按钮被切掉。
|
||||||
- **方案**:用隐藏 canvas 2d 绘制一张 5:4(500×400 逻辑像素)的专用分享海报,在 `onShareAppMessage` 里作为 `imageUrl` 返回;提前在 `_saveAndShowCompletion` 弹窗数据就绪后生成,避免分享时现场绘制延迟。
|
- **方案**:用隐藏 canvas 2d 绘制一张 5:4(500×400 逻辑像素)的专用分享海报,在 `onShareAppMessage` 里作为 `imageUrl` 返回;提前在 `_saveAndShowCompletion` 弹窗数据就绪后生成,避免分享时现场绘制延迟。
|
||||||
- **海报内容**:浅灰底 + 顶部品牌 + 白色圆角成绩卡 + 绿色渐变星星图标 + 状态文案 + 大数字时长 + 连续打卡(如有)+ 底部"扫码一起打卡"引导 + 右下角小程序二维码。
|
- **海报内容(初版)**:浅灰底 + 顶部品牌 + 白色圆角成绩卡 + 绿色渐变星星图标 + 状态文案 + 大数字时长 + 连续打卡(如有)+ 底部"扫码一起打卡"引导 + 右下角小程序二维码。
|
||||||
- **文件改动**:
|
- **文件改动**:
|
||||||
- `pages/timer/timer.wxml`:添加隐藏 `<canvas type="2d" id="sharePoster">`
|
- `pages/timer/timer.wxml`:添加隐藏 `<canvas type="2d" id="sharePoster">`
|
||||||
- `pages/timer/timer.wxss`:`.share-poster-canvas` 移出视口、不响应点击
|
- `pages/timer/timer.wxss`:`.share-poster-canvas` 移出视口、不响应点击
|
||||||
- `pages/timer/timer.js`:新增 `_drawSharePoster()`、`_formatShareDuration()`;`_saveAndShowCompletion()` 里 setData 后预生成;`onShareAppMessage()` 返回 `imageUrl: this._shareImageUrl`;`onTrainAgain()` 清空旧图防止复用
|
- `pages/timer/timer.js`:新增 `_drawSharePoster()`、`_formatShareDuration()`;`_saveAndShowCompletion()` 里 setData 后预生成;`onShareAppMessage()` 返回 `imageUrl: this._shareImageUrl`;`onTrainAgain()` 清空旧图防止复用
|
||||||
- `static/share-qr.jpg`:加入用户提供的小程序二维码资源
|
- `static/share-qr.jpg`:加入用户提供的小程序二维码资源
|
||||||
- **注意**:导出用 jpg quality 0.9、dest 500×400,控制文件在 128KB 以内;二维码路径 `/static/share-qr.jpg` 为相对小程序根目录;若二维码加载失败仍导出无二维码的海报,保证分享入口不灰。
|
- **注意**:导出用 jpg quality 0.9、dest 500×400,控制文件在 128KB 以内;二维码路径 `/static/share-qr.jpg` 为相对小程序根目录;若二维码加载失败仍导出无二维码的海报,保证分享入口不灰。
|
||||||
|
|
||||||
|
## 调整:分享海报(实测反馈)
|
||||||
|
- **反馈**:① 大数字(分钟数)压住了上方"完成"状态文案;② 顶部小程序名称多余;③ 要删二维码。
|
||||||
|
- **根因**:初版状态文案 `y=172`(字号22)、大数字基线 `y=230` 且字号最大 90px,数字顶部≈165 落入状态文案区域(150~172),多位数分钟(如纯分钟 "6"→90px)直接压字。
|
||||||
|
- **修复**(仅改 `_drawSharePoster` 布局):删除顶部"平板的支撑"品牌名;删除右下角二维码及依赖它的"扫码一起打卡"引导(连带 `static/share-qr.jpg` 也删,不再引用);重排垂直节奏——卡片放大(42~358)、图标 `circleY=104`、状态文案 `y=184`(24px)、大数字基线 `y=286`(上限84px,顶部≈226,与状态文案下沿 192 间距 34 安全)、连续打卡 `y=332`;导出改为绘制完直接 `canvasToTempFilePath`,无需等图片加载。
|
||||||
|
- **结论**:海报现为"成绩卡 + 图标 + 状态 + 大数字 + 连续打卡"的极简版,无品牌名无二维码。
|
||||||
|
|||||||
+26
-64
@@ -1037,30 +1037,23 @@ Page({
|
|||||||
ctx.fillStyle = '#F7F8FA'
|
ctx.fillStyle = '#F7F8FA'
|
||||||
ctx.fillRect(0, 0, W, H)
|
ctx.fillRect(0, 0, W, H)
|
||||||
|
|
||||||
// --- top brand ---
|
|
||||||
ctx.fillStyle = '#888888'
|
|
||||||
ctx.font = 'normal 400 18px sans-serif'
|
|
||||||
ctx.textAlign = 'center'
|
|
||||||
ctx.textBaseline = 'alphabetic'
|
|
||||||
ctx.fillText('平板的支撑', W / 2, 36)
|
|
||||||
|
|
||||||
// --- main card shadow ---
|
// --- main card shadow ---
|
||||||
ctx.save()
|
ctx.save()
|
||||||
ctx.fillStyle = 'rgba(0,0,0,0.06)'
|
ctx.fillStyle = 'rgba(0,0,0,0.06)'
|
||||||
roundRect(38, 54, 424, 250, 22)
|
roundRect(38, 44, 424, 320, 22)
|
||||||
ctx.fill()
|
ctx.fill()
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
|
||||||
// --- main card ---
|
// --- main card ---
|
||||||
ctx.save()
|
ctx.save()
|
||||||
ctx.fillStyle = '#FFFFFF'
|
ctx.fillStyle = '#FFFFFF'
|
||||||
roundRect(35, 52, 430, 246, 20)
|
roundRect(35, 42, 430, 316, 20)
|
||||||
ctx.fill()
|
ctx.fill()
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
|
||||||
// --- gradient circle icon (same feel as completion-emoji-wrap) ---
|
// --- gradient circle icon (same feel as completion-emoji-wrap) ---
|
||||||
const circleX = W / 2
|
const circleX = W / 2
|
||||||
const circleY = 108
|
const circleY = 104
|
||||||
const circleR = 40
|
const circleR = 40
|
||||||
const grad = ctx.createLinearGradient(circleX - circleR, circleY - circleR, circleX + circleR, circleY + circleR)
|
const grad = ctx.createLinearGradient(circleX - circleR, circleY - circleR, circleX + circleR, circleY + circleR)
|
||||||
grad.addColorStop(0, '#4CAF50')
|
grad.addColorStop(0, '#4CAF50')
|
||||||
@@ -1075,23 +1068,24 @@ Page({
|
|||||||
drawStar(circleX, circleY, 20, 8, 5)
|
drawStar(circleX, circleY, 20, 8, 5)
|
||||||
ctx.fill()
|
ctx.fill()
|
||||||
|
|
||||||
// --- status text ---
|
// --- status text (kept well above the big number so a multi-digit
|
||||||
|
// minute count never overlaps it) ---
|
||||||
ctx.fillStyle = '#333333'
|
ctx.fillStyle = '#333333'
|
||||||
ctx.font = 'normal 600 22px sans-serif'
|
ctx.font = 'normal 600 24px sans-serif'
|
||||||
ctx.textAlign = 'center'
|
ctx.textAlign = 'center'
|
||||||
ctx.textBaseline = 'alphabetic'
|
ctx.textBaseline = 'alphabetic'
|
||||||
ctx.fillText(message, W / 2, 172)
|
ctx.fillText(message, W / 2, 184)
|
||||||
|
|
||||||
// --- big number + unit ---
|
// --- big number + unit ---
|
||||||
const fmt = this._formatShareDuration(elapsed)
|
const fmt = this._formatShareDuration(elapsed)
|
||||||
const numLen = fmt.num.length
|
const numLen = fmt.num.length
|
||||||
const numSize = numLen <= 2 ? 90 : numLen <= 3 ? 76 : numLen <= 4 ? 60 : 48
|
const numSize = numLen <= 2 ? 84 : numLen <= 3 ? 70 : numLen <= 4 ? 56 : 44
|
||||||
ctx.font = `normal 800 ${numSize}px sans-serif`
|
ctx.font = `normal 800 ${numSize}px sans-serif`
|
||||||
ctx.textAlign = 'right'
|
ctx.textAlign = 'right'
|
||||||
ctx.textBaseline = 'alphabetic'
|
ctx.textBaseline = 'alphabetic'
|
||||||
ctx.fillStyle = primary
|
ctx.fillStyle = primary
|
||||||
const numX = W / 2 + 4
|
const numX = W / 2 + 4
|
||||||
const numY = 230
|
const numY = 286
|
||||||
ctx.fillText(fmt.num, numX, numY)
|
ctx.fillText(fmt.num, numX, numY)
|
||||||
|
|
||||||
ctx.fillStyle = '#666666'
|
ctx.fillStyle = '#666666'
|
||||||
@@ -1104,57 +1098,25 @@ Page({
|
|||||||
ctx.fillStyle = primary
|
ctx.fillStyle = primary
|
||||||
ctx.font = 'normal 500 18px sans-serif'
|
ctx.font = 'normal 500 18px sans-serif'
|
||||||
ctx.textAlign = 'center'
|
ctx.textAlign = 'center'
|
||||||
ctx.fillText(`连续打卡 ${streak} 天`, W / 2, 272)
|
ctx.fillText(`连续打卡 ${streak} 天`, W / 2, 332)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- bottom left: CTA text ---
|
// --- export (no QR; draw immediately) ---
|
||||||
ctx.textAlign = 'left'
|
wx.canvasToTempFilePath({
|
||||||
ctx.textBaseline = 'alphabetic'
|
canvas,
|
||||||
ctx.fillStyle = '#333333'
|
width: W,
|
||||||
ctx.font = 'normal 700 22px sans-serif'
|
height: H,
|
||||||
ctx.fillText('扫码一起打卡', 48, 338)
|
destWidth: W,
|
||||||
|
destHeight: H,
|
||||||
ctx.fillStyle = '#999999'
|
fileType: 'jpg',
|
||||||
ctx.font = 'normal 400 15px sans-serif'
|
quality: 0.9,
|
||||||
ctx.fillText('平板的支撑 · 每天进步一点点', 48, 366)
|
success: (r) => {
|
||||||
|
this._shareImageUrl = r.tempFilePath
|
||||||
// --- bottom right: QR code ---
|
},
|
||||||
const qrSize = 88
|
fail: (err) => {
|
||||||
const qrX = W - 48 - qrSize
|
console.warn('[sharePoster] export failed', err)
|
||||||
const qrY = 308
|
}
|
||||||
const doExport = () => {
|
})
|
||||||
wx.canvasToTempFilePath({
|
|
||||||
canvas,
|
|
||||||
width: W,
|
|
||||||
height: H,
|
|
||||||
destWidth: W,
|
|
||||||
destHeight: H,
|
|
||||||
fileType: 'jpg',
|
|
||||||
quality: 0.9,
|
|
||||||
success: (r) => {
|
|
||||||
this._shareImageUrl = r.tempFilePath
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.warn('[sharePoster] export failed', err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const img = canvas.createImage()
|
|
||||||
img.src = '/static/share-qr.jpg'
|
|
||||||
img.onload = () => {
|
|
||||||
ctx.save()
|
|
||||||
ctx.fillStyle = '#FFFFFF'
|
|
||||||
roundRect(qrX - 6, qrY - 6, qrSize + 12, qrSize + 12, 8)
|
|
||||||
ctx.fill()
|
|
||||||
ctx.drawImage(img, qrX, qrY, qrSize, qrSize)
|
|
||||||
ctx.restore()
|
|
||||||
doExport()
|
|
||||||
}
|
|
||||||
img.onerror = () => {
|
|
||||||
// 二维码加载失败时仍导出一张无二维码的海报,避免分享按钮失效
|
|
||||||
doExport()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 52 KiB |
Reference in New Issue
Block a user