141 lines
3.9 KiB
TypeScript
141 lines
3.9 KiB
TypeScript
/**
|
||
* 生成好友之树分享图(Canvas 绘制,可下载发到微信朋友圈)
|
||
*/
|
||
|
||
export interface ShareTreeData {
|
||
friendName: string
|
||
myName: string
|
||
stageIndex: number
|
||
stageName: string
|
||
stageEmoji: string
|
||
messageCount: number
|
||
waterCount: number
|
||
totalScore: number
|
||
hue: number
|
||
}
|
||
|
||
function hsl(h: number, s: number, l: number): string {
|
||
return `hsl(${h}, ${s}%, ${l}%)`
|
||
}
|
||
|
||
/** 把树画到 canvas 上 */
|
||
function drawTree(ctx: CanvasRenderingContext2D, cx: number, baseY: number, stageIndex: number, hue: number) {
|
||
if (stageIndex === 0) {
|
||
// 种子
|
||
ctx.fillStyle = hsl(25, 45, 40)
|
||
ctx.beginPath()
|
||
ctx.ellipse(cx, baseY, 14, 10, 0, 0, Math.PI * 2)
|
||
ctx.fill()
|
||
return
|
||
}
|
||
const trunkW = 4 + stageIndex * 2.2
|
||
const canopyR = 22 + stageIndex * 9
|
||
const canopyN = 3 + stageIndex
|
||
|
||
// 树干
|
||
ctx.fillStyle = hsl(25, 45, 35 + stageIndex * 2)
|
||
ctx.fillRect(cx - trunkW / 2, baseY - 90, trunkW, 90)
|
||
|
||
// 树冠(多个圆叠加)
|
||
ctx.fillStyle = hsl(hue, 55, 42)
|
||
ctx.beginPath()
|
||
ctx.arc(cx, baseY - 100, canopyR, 0, Math.PI * 2)
|
||
ctx.fill()
|
||
for (let i = 1; i < canopyN; i++) {
|
||
const offset = i % 2 === 1 ? 1 : -1
|
||
const layer = Math.ceil(i / 2)
|
||
ctx.fillStyle = hsl(hue, 55, 42 + (i % 2) * 6)
|
||
ctx.beginPath()
|
||
ctx.arc(
|
||
cx + offset * canopyR * 0.6 * layer,
|
||
baseY - 100 + layer * canopyR * 0.3,
|
||
canopyR * (0.75 - layer * 0.1),
|
||
0, Math.PI * 2
|
||
)
|
||
ctx.fill()
|
||
}
|
||
// 高光
|
||
ctx.fillStyle = hsl(hue, 60, 60)
|
||
ctx.globalAlpha = 0.3
|
||
ctx.beginPath()
|
||
ctx.arc(cx - canopyR * 0.3, baseY - 100 - canopyR * 0.3, canopyR * 0.3, 0, Math.PI * 2)
|
||
ctx.fill()
|
||
ctx.globalAlpha = 1
|
||
}
|
||
|
||
/** 生成完整分享卡片,返回 dataURL */
|
||
export function generateTreeShareCard(data: ShareTreeData): string {
|
||
const W = 600, H = 800
|
||
const canvas = document.createElement('canvas')
|
||
canvas.width = W
|
||
canvas.height = H
|
||
const ctx = canvas.getContext('2d')!
|
||
|
||
// 背景渐变
|
||
const bg = ctx.createLinearGradient(0, 0, 0, H)
|
||
bg.addColorStop(0, '#E8F5E9')
|
||
bg.addColorStop(0.5, '#C8E6C9')
|
||
bg.addColorStop(1, '#A5D6A7')
|
||
ctx.fillStyle = bg
|
||
ctx.fillRect(0, 0, W, H)
|
||
|
||
// 顶部光晕
|
||
const glow = ctx.createRadialGradient(W / 2, 200, 0, W / 2, 200, 300)
|
||
glow.addColorStop(0, 'rgba(255,255,255,0.5)')
|
||
glow.addColorStop(1, 'rgba(255,255,255,0)')
|
||
ctx.fillStyle = glow
|
||
ctx.fillRect(0, 0, W, 400)
|
||
|
||
// logo + 标题
|
||
ctx.fillStyle = '#004D40'
|
||
ctx.font = 'bold 28px -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif'
|
||
ctx.textAlign = 'center'
|
||
ctx.fillText('🌿 我们在青叶种了一棵树', W / 2, 70)
|
||
|
||
// 名字
|
||
ctx.font = '20px -apple-system, "PingFang SC", sans-serif'
|
||
ctx.fillStyle = '#00695C'
|
||
ctx.fillText(`${data.myName} × ${data.friendName}`, W / 2, 105)
|
||
|
||
// 地面
|
||
ctx.fillStyle = 'rgba(139,90,43,0.15)'
|
||
ctx.beginPath()
|
||
ctx.ellipse(W / 2, 560, 140, 16, 0, 0, Math.PI * 2)
|
||
ctx.fill()
|
||
|
||
// 画树
|
||
drawTree(ctx, W / 2, 560, data.stageIndex, data.hue)
|
||
|
||
// 阶段标签
|
||
ctx.font = 'bold 22px sans-serif'
|
||
ctx.fillStyle = '#00796B'
|
||
ctx.fillText(`${data.stageEmoji} ${data.stageName} Lv.${data.stageIndex + 1}`, W / 2, 620)
|
||
|
||
// 统计三栏
|
||
const stats = [
|
||
{ label: '条消息', value: data.messageCount },
|
||
{ label: '次浇水', value: data.waterCount },
|
||
{ label: '成长值', value: data.totalScore },
|
||
]
|
||
const statY = 670
|
||
stats.forEach((s, i) => {
|
||
const x = W / 4 + (W / 4) * i - 30
|
||
ctx.fillStyle = '#009688'
|
||
ctx.font = 'bold 26px sans-serif'
|
||
ctx.fillText(String(s.value), x, statY)
|
||
ctx.fillStyle = '#5F7A74'
|
||
ctx.font = '13px sans-serif'
|
||
ctx.fillText(s.label, x, statY + 22)
|
||
})
|
||
|
||
// 底部邀请
|
||
ctx.fillStyle = '#004D40'
|
||
ctx.font = '15px sans-serif'
|
||
ctx.fillText('来青叶,和你在意的人一起种树', W / 2, 745)
|
||
ctx.font = '12px sans-serif'
|
||
ctx.fillStyle = '#5F7A74'
|
||
ctx.fillText('微信给不了你的,是一片会生长的关系', W / 2, 770)
|
||
|
||
return canvas.toDataURL('image/png')
|
||
}
|