From 90017d3582916dcc0409dbaff9f5c2b4f47f8b05 Mon Sep 17 00:00:00 2001 From: zihanjian Date: Sat, 7 Jun 2025 21:38:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A8=E6=80=81=E8=B0=83=E6=95=B4CompletionC?= =?UTF-8?q?ard=E7=BB=84=E4=BB=B6=E7=9A=84=E7=94=BB=E5=B8=83=E5=B0=BA?= =?UTF-8?q?=E5=AF=B8=E5=92=8C=E8=83=8C=E6=99=AF=E6=A0=B7=E5=BC=8F=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8B=BC=E8=B1=86=E5=9B=BE=E5=92=8C=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=85=A7=E7=89=87=E7=9A=84=E5=B1=95=E7=A4=BA=E6=95=88?= =?UTF-8?q?=E6=9E=9C=EF=BC=8C=E5=A2=9E=E5=BC=BA=E4=BF=A1=E6=81=AF=E5=8C=BA?= =?UTF-8?q?=E5=9F=9F=E7=9A=84=E5=8F=AF=E8=AF=BB=E6=80=A7=E5=92=8C=E7=BE=8E?= =?UTF-8?q?=E8=A7=82=E6=80=A7=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/CompletionCard.tsx | 313 ++++++++++++++++++------------ 1 file changed, 187 insertions(+), 126 deletions(-) diff --git a/src/components/CompletionCard.tsx b/src/components/CompletionCard.tsx index 90ccc5b..2117ac2 100644 --- a/src/components/CompletionCard.tsx +++ b/src/components/CompletionCard.tsx @@ -148,158 +148,219 @@ const CompletionCard: React.FC = ({ const thumbnailDataURL = generateThumbnail(); const isUsingPixelArt = userPhoto === thumbnailDataURL; - // 设置画布尺寸 (比例适合分享) - const cardWidth = 800; - const cardHeight = 1000; + // 动态计算画布尺寸 + const cardWidth = 720; + const topPadding = 120; // 顶部标题区域 + const imageSize = Math.min(cardWidth * 0.9, 600); // 主图片尺寸,最大600px + const bottomInfoHeight = 120; // 底部信息区域 + const bottomBrandHeight = 80; // 底部品牌区域 + const padding = 40; // 各区域间的间距 + + const cardHeight = topPadding + imageSize + bottomInfoHeight + bottomBrandHeight + padding * 2; canvas.width = cardWidth; canvas.height = cardHeight; - // 渐变背景 - const gradient = ctx.createLinearGradient(0, 0, 0, cardHeight); - gradient.addColorStop(0, '#667eea'); - gradient.addColorStop(1, '#764ba2'); - ctx.fillStyle = gradient; - ctx.fillRect(0, 0, cardWidth, cardHeight); - return new Promise((resolve) => { // 加载用户照片/拼豆图 const userImg = new Image(); userImg.onload = () => { if (isUsingPixelArt) { - // 使用拼豆原图的布局 - 重新设计让图片占主要空间 - const padding = 40; - const topSpace = 80; - const bottomSpace = 120; + // ===== 拼豆原图模式:原图占主导 ===== - // 计算拼豆图尺寸(占据大部分空间) - const availableHeight = cardHeight - topSpace - bottomSpace; - const availableWidth = cardWidth - padding * 2; - const imageSize = Math.min(availableWidth, availableHeight); - - const imageX = (cardWidth - imageSize) / 2; - const imageY = topSpace; - - // 标题(放在顶部,比较小) - ctx.fillStyle = '#fff'; - ctx.font = 'bold 24px Arial, sans-serif'; - ctx.textAlign = 'center'; - ctx.fillText('🎉 作品完成!🎉', cardWidth / 2, 40); + // 深色渐变背景,更有质感 + const gradient = ctx.createLinearGradient(0, 0, 0, cardHeight); + gradient.addColorStop(0, '#1a1a2e'); + gradient.addColorStop(0.3, '#16213e'); + gradient.addColorStop(0.7, '#0f3460'); + gradient.addColorStop(1, '#533483'); + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, cardWidth, cardHeight); - // 绘制拼豆图(大图) - ctx.fillStyle = '#fff'; + // 计算拼豆图尺寸(占据80%的高度,居中显示) + const imageMaxSize = Math.min(cardWidth * 0.9, cardHeight * 0.75); + const imageSize = imageMaxSize; + const imageX = (cardWidth - imageSize) / 2; + const imageY = (cardHeight - imageSize) / 2 - 20; // 稍微往上偏移 + + // 绘制主图片的装饰背景和阴影 + ctx.save(); + // 外层光晕效果 + const glowGradient = ctx.createRadialGradient( + imageX + imageSize/2, imageY + imageSize/2, imageSize/2, + imageX + imageSize/2, imageY + imageSize/2, imageSize/2 + 30 + ); + glowGradient.addColorStop(0, 'rgba(255,255,255,0.1)'); + glowGradient.addColorStop(1, 'rgba(255,255,255,0)'); + ctx.fillStyle = glowGradient; + ctx.fillRect(imageX - 30, imageY - 30, imageSize + 60, imageSize + 60); + + // 白色边框背景 + ctx.fillStyle = '#ffffff'; ctx.shadowColor = 'rgba(0,0,0,0.3)'; + ctx.shadowBlur = 25; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 15; + const borderWidth = 12; + ctx.fillRect(imageX - borderWidth, imageY - borderWidth, + imageSize + borderWidth * 2, imageSize + borderWidth * 2); + ctx.restore(); + + // 绘制拼豆原图 + ctx.drawImage(userImg, imageX, imageY, imageSize, imageSize); + + // 顶部区域:简洁的完成标识 + ctx.fillStyle = '#ffffff'; + ctx.font = 'bold 28px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; + ctx.textAlign = 'center'; + ctx.shadowColor = 'rgba(0,0,0,0.3)'; + ctx.shadowBlur = 8; + ctx.fillText('🎉 作品完成 🎉', cardWidth / 2, 80); + ctx.shadowBlur = 0; + + // 底部信息区域:透明背景卡片 + const infoY = imageY + imageSize + 50; + const infoHeight = 120; + const infoX = 40; + const infoWidth = cardWidth - 80; + + // 半透明背景 + ctx.fillStyle = 'rgba(255,255,255,0.15)'; + ctx.fillRect(infoX, infoY, infoWidth, infoHeight); + + // 信息文字 + ctx.fillStyle = '#ffffff'; + ctx.font = 'bold 20px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText(`⏱️ ${formatTime(totalElapsedTime)}`, cardWidth / 2, infoY + 35); + + ctx.font = '18px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; + ctx.fillStyle = 'rgba(255,255,255,0.9)'; + ctx.fillText(`🔗 完成 ${totalBeads} 颗豆子`, cardWidth / 2, infoY + 65); + + // 底部品牌信息 + ctx.font = '14px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; + ctx.fillStyle = 'rgba(255,255,255,0.7)'; + ctx.fillText('七卡瓦拼豆底稿生成器', cardWidth / 2, cardHeight - 50); + ctx.font = '12px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; + ctx.fillStyle = 'rgba(255,255,255,0.5)'; + ctx.fillText('perlerbeads.zippland.com', cardWidth / 2, cardHeight - 25); + + resolve(canvas.toDataURL('image/jpeg', 0.95)); + + } else { + // ===== 用户照片模式:照片占主导 ===== + + // 温暖渐变背景 + const gradient = ctx.createLinearGradient(0, 0, 0, cardHeight); + gradient.addColorStop(0, '#ff9a9e'); + gradient.addColorStop(0.3, '#fecfef'); + gradient.addColorStop(0.7, '#fecfef'); + gradient.addColorStop(1, '#ff9a9e'); + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, cardWidth, cardHeight); + + // 计算照片尺寸(占据大部分空间) + const photoMaxSize = Math.min(cardWidth * 0.85, cardHeight * 0.7); + const photoSize = photoMaxSize; + const photoX = (cardWidth - photoSize) / 2; + const photoY = (cardHeight - photoSize) / 2 - 30; + + // 绘制照片装饰背景和阴影 + ctx.save(); + // 外层装饰边框 + ctx.strokeStyle = 'rgba(255,255,255,0.8)'; + ctx.lineWidth = 8; + ctx.strokeRect(photoX - 15, photoY - 15, photoSize + 30, photoSize + 30); + + // 内层白色边框背景 + ctx.fillStyle = '#ffffff'; + ctx.shadowColor = 'rgba(0,0,0,0.2)'; + ctx.shadowBlur = 20; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 10; + ctx.fillRect(photoX - 12, photoY - 12, photoSize + 24, photoSize + 24); + ctx.restore(); + + // 绘制矩形照片 + ctx.drawImage(userImg, photoX, photoY, photoSize, photoSize); + + // 顶部完成标识 + ctx.fillStyle = '#ffffff'; + ctx.font = 'bold 32px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; + ctx.textAlign = 'center'; + ctx.shadowColor = 'rgba(0,0,0,0.3)'; + ctx.shadowBlur = 8; + ctx.fillText('🎉 拼豆达成', cardWidth / 2, 100); + ctx.shadowBlur = 0; + + // 底部信息卡片 + const infoCardY = photoY + photoSize + 40; + const cardHeight2 = 140; + const cardX = 60; + const cardWidth2 = cardWidth - 120; + + // 信息卡片背景 + ctx.fillStyle = 'rgba(255,255,255,0.95)'; + ctx.shadowColor = 'rgba(0,0,0,0.1)'; ctx.shadowBlur = 15; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 8; - ctx.fillRect(imageX - 8, imageY - 8, imageSize + 16, imageSize + 16); + ctx.fillRect(cardX, infoCardY, cardWidth2, cardHeight2); ctx.shadowBlur = 0; - ctx.drawImage(userImg, imageX, imageY, imageSize, imageSize); - // 时长和豆子数信息(紧贴图片下方) - ctx.fillStyle = '#fff'; - ctx.font = 'bold 18px Arial, sans-serif'; - ctx.fillText(`⏱️ ${formatTime(totalElapsedTime)}`, cardWidth / 2, imageY + imageSize + 30); - ctx.font = '16px Arial, sans-serif'; - ctx.fillText(`🔗 ${totalBeads}颗豆子`, cardWidth / 2, imageY + imageSize + 55); - - // 网站信息(底部,更小) - ctx.font = '14px Arial, sans-serif'; - ctx.fillText('来自 七卡瓦拼豆底稿生成器', cardWidth / 2, cardHeight - 60); - ctx.font = '12px Arial, sans-serif'; - ctx.fillStyle = 'rgba(255,255,255,0.8)'; - ctx.fillText('perlerbeads.zippland.com', cardWidth / 2, cardHeight - 35); - - // 装饰元素(更小,分布在四角) - const cornerEmojis = ['🎨', '🌟', '🎊', '✨']; - ctx.font = '20px Arial, sans-serif'; - ctx.fillStyle = '#fff'; - - // 四个角落的装饰 - const cornerPositions = [ - { x: 60, y: 120 }, // 左上 - { x: cardWidth - 60, y: 120 }, // 右上 - { x: 60, y: imageY + imageSize - 40 }, // 左下 - { x: cardWidth - 60, y: imageY + imageSize - 40 } // 右下 - ]; - - cornerEmojis.forEach((emoji, index) => { - if (cornerPositions[index]) { - ctx.fillText(emoji, cornerPositions[index].x, cornerPositions[index].y); - } - }); - - resolve(canvas.toDataURL('image/jpeg', 0.9)); - } else { - // 原来的用户照片布局 - const photoSize = 200; - const photoX = (cardWidth - photoSize) / 2; - const photoY = 60; - - ctx.save(); - ctx.beginPath(); - ctx.arc(photoX + photoSize/2, photoY + photoSize/2, photoSize/2, 0, Math.PI * 2); - ctx.clip(); - ctx.drawImage(userImg, photoX, photoY, photoSize, photoSize); - ctx.restore(); - - // 绘制圆形边框 - ctx.strokeStyle = '#fff'; - ctx.lineWidth = 4; - ctx.beginPath(); - ctx.arc(photoX + photoSize/2, photoY + photoSize/2, photoSize/2, 0, Math.PI * 2); - ctx.stroke(); - - // 标题 - ctx.fillStyle = '#fff'; - ctx.font = 'bold 32px Arial, sans-serif'; + // 信息文字 + ctx.fillStyle = '#333333'; + ctx.font = 'bold 22px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; ctx.textAlign = 'center'; - ctx.fillText('🎉 作品完成!', cardWidth / 2, photoY + photoSize + 60); + ctx.fillText(`⏱️ 总用时 ${formatTime(totalElapsedTime)}`, cardWidth / 2, infoCardY + 40); + + ctx.font = '20px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; + ctx.fillStyle = '#666666'; + ctx.fillText(`🔗 共完成 ${totalBeads} 颗豆子`, cardWidth / 2, infoCardY + 75); - // 时长和豆子数 - ctx.font = '22px Arial, sans-serif'; - ctx.fillText(`总用时:${formatTime(totalElapsedTime)}`, cardWidth / 2, photoY + photoSize + 90); - ctx.font = '18px Arial, sans-serif'; - ctx.fillText(`共拼了 ${totalBeads} 颗豆子`, cardWidth / 2, photoY + photoSize + 115); - - // 原图缩略图 + // 添加小的拼豆原图作为装饰 if (thumbnailDataURL) { const thumbnailImg = new Image(); thumbnailImg.onload = () => { - const thumbSize = 180; - const thumbX = (cardWidth - thumbSize) / 2; - const thumbY = photoY + photoSize + 155; + const thumbSize = 60; + const thumbX = cardWidth / 2 - thumbSize / 2; + const thumbY = infoCardY + 90; - // 绘制缩略图背景 - ctx.fillStyle = '#fff'; - ctx.fillRect(thumbX - 10, thumbY - 10, thumbSize + 20, thumbSize + 20); - ctx.drawImage(thumbnailImg, thumbX, thumbY, thumbSize, thumbSize); + // 绘制小缩略图背景 + ctx.fillStyle = '#ffffff'; + ctx.fillRect(thumbX - 3, thumbY - 3, thumbSize + 6, thumbSize + 6); + + // 绘制小缩略图 + ctx.drawImage(thumbnailImg, thumbX, thumbY, thumbSize, thumbSize); + + // 缩略图边框 + ctx.strokeStyle = '#ffffff'; + ctx.lineWidth = 3; + ctx.strokeRect(thumbX - 3, thumbY - 3, thumbSize + 6, thumbSize + 6); - // 缩略图标题 - ctx.fillStyle = '#fff'; - ctx.font = '18px Arial, sans-serif'; - ctx.fillText('作品图案', cardWidth / 2, thumbY + thumbSize + 40); + // 底部品牌信息 + ctx.font = '14px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; + ctx.fillStyle = 'rgba(255,255,255,0.8)'; + ctx.textAlign = 'center'; + ctx.fillText('七卡瓦拼豆底稿生成器', cardWidth / 2, cardHeight - 50); + ctx.font = '12px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; + ctx.fillStyle = 'rgba(255,255,255,0.6)'; + ctx.fillText('perlerbeads.zippland.com', cardWidth / 2, cardHeight - 25); - // 网站信息 - ctx.font = '16px Arial, sans-serif'; - ctx.fillText('来自 七卡瓦拼豆底稿生成器', cardWidth / 2, cardHeight - 80); - ctx.fillText('perlerbeads.zippland.com', cardWidth / 2, cardHeight - 50); - - // 装饰元素 - const decorEmojis = ['🎨', '🌟', '🎊', '💫']; - ctx.font = '24px Arial, sans-serif'; - decorEmojis.forEach((emoji, index) => { - const angle = (index * Math.PI * 2) / decorEmojis.length; - const radius = 120; - const x = cardWidth / 2 + Math.cos(angle) * radius; - const y = photoY + photoSize / 2 + Math.sin(angle) * radius; - ctx.fillText(emoji, x, y); - }); - - resolve(canvas.toDataURL('image/jpeg', 0.9)); + resolve(canvas.toDataURL('image/jpeg', 0.95)); }; thumbnailImg.src = thumbnailDataURL; } else { - resolve(canvas.toDataURL('image/jpeg', 0.9)); + // 底部品牌信息 + ctx.font = '14px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; + ctx.fillStyle = 'rgba(255,255,255,0.8)'; + ctx.textAlign = 'center'; + ctx.fillText('七卡瓦拼豆底稿生成器', cardWidth / 2, cardHeight - 50); + ctx.font = '12px system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; + ctx.fillStyle = 'rgba(255,255,255,0.6)'; + ctx.fillText('perlerbeads.zippland.com', cardWidth / 2, cardHeight - 25); + + resolve(canvas.toDataURL('image/jpeg', 0.95)); } } }; @@ -326,7 +387,7 @@ const CompletionCard: React.FC = ({

- 🎉 作品完成! + 🎉 作品完成 🎉

总用时:{formatTime(totalElapsedTime)}