diff --git a/apps/web/public/android-chrome-192x192.png b/apps/web/public/android-chrome-192x192.png index 455a657f..94eca47c 100644 Binary files a/apps/web/public/android-chrome-192x192.png and b/apps/web/public/android-chrome-192x192.png differ diff --git a/apps/web/public/android-chrome-512x512.png b/apps/web/public/android-chrome-512x512.png index 4ea85978..5280dc1d 100644 Binary files a/apps/web/public/android-chrome-512x512.png and b/apps/web/public/android-chrome-512x512.png differ diff --git a/apps/web/public/apple-touch-icon.png b/apps/web/public/apple-touch-icon.png index 64e6ba4d..d2193c13 100644 Binary files a/apps/web/public/apple-touch-icon.png and b/apps/web/public/apple-touch-icon.png differ diff --git a/apps/web/public/favicon-16x16.png b/apps/web/public/favicon-16x16.png index 3137db24..5b14c7b0 100644 Binary files a/apps/web/public/favicon-16x16.png and b/apps/web/public/favicon-16x16.png differ diff --git a/apps/web/public/favicon-32x32.png b/apps/web/public/favicon-32x32.png index 3abda3d7..1c0235d8 100644 Binary files a/apps/web/public/favicon-32x32.png and b/apps/web/public/favicon-32x32.png differ diff --git a/apps/web/public/favicon-48x48.png b/apps/web/public/favicon-48x48.png index 0fe56ace..b72a3f06 100644 Binary files a/apps/web/public/favicon-48x48.png and b/apps/web/public/favicon-48x48.png differ diff --git a/apps/web/public/favicon.ico b/apps/web/public/favicon.ico index 3abda3d7..1c0235d8 100644 Binary files a/apps/web/public/favicon.ico and b/apps/web/public/favicon.ico differ diff --git a/logo.jpg b/logo.jpg new file mode 100644 index 00000000..b7eacf47 Binary files /dev/null and b/logo.jpg differ diff --git a/scripts/generate-favicon.ts b/scripts/generate-favicon.ts index 4f6a8274..f59ecbee 100644 --- a/scripts/generate-favicon.ts +++ b/scripts/generate-favicon.ts @@ -1,70 +1,52 @@ import { existsSync, mkdirSync, writeFileSync } from 'node:fs' -import { join } from 'node:path' +import path, { join } from 'node:path' import sharp from 'sharp' -// 创建 SVG favicon 设计 -function createFaviconSVG(size: number) { - const iconSize = size * 0.8 // 图标占 80% 的空间 - const padding = (size - iconSize) / 2 +const __dirname = path.dirname(new URL(import.meta.url).pathname) +// 创建圆角遮罩 +function createRoundedCornersMask(size: number, cornerRadius: number) { + const r = cornerRadius return ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + ` } +// 为图片添加圆角 +async function addRoundedCorners( + imageBuffer: Buffer, + size: number, +): Promise { + // 计算圆角半径,约为尺寸的 12% + const cornerRadius = Math.round(size * 0.12) + + const maskSvg = createRoundedCornersMask(size, cornerRadius) + const maskBuffer = Buffer.from(maskSvg) + + return sharp(imageBuffer) + .composite([ + { + input: maskBuffer, + blend: 'dest-in', + }, + ]) + .png() + .toBuffer() +} + // 生成不同尺寸的 favicon export async function generateFavicons() { + const logoPath = join(__dirname, '../logo.jpg') const outputDir = join(process.cwd(), 'public') + + // 检查 logo 文件是否存在 + if (!existsSync(logoPath)) { + throw new Error('Logo file not found: logo.jpg') + } + if (!existsSync(outputDir)) { mkdirSync(outputDir, { recursive: true }) } @@ -79,32 +61,45 @@ export async function generateFavicons() { ] try { - // 生成 ICO 文件(包含多个尺寸) - const icoSizes = [16, 32, 48] - const icoBuffers: Buffer[] = [] - - for (const size of icoSizes) { - const svgContent = createFaviconSVG(size) - const buffer = await sharp(Buffer.from(svgContent)).png().toBuffer() - icoBuffers.push(buffer) - } + // 读取原始 logo 图片 + const logoBuffer = await sharp(logoPath).jpeg({ quality: 100 }).toBuffer() // 生成各种尺寸的 PNG 文件 for (const { size, name } of sizes) { - const svgContent = createFaviconSVG(size) - const buffer = await sharp(Buffer.from(svgContent)).png().toBuffer() + const resizedBuffer = await sharp(logoBuffer) + .resize(size, size, { + fit: 'cover', + position: 'center', + }) + .png({ + quality: 100, + compressionLevel: 6, + }) + .toBuffer() + + // 添加圆角效果 + const roundedBuffer = await addRoundedCorners(resizedBuffer, size) const outputPath = join(outputDir, name) - writeFileSync(outputPath, buffer) + writeFileSync(outputPath, roundedBuffer) console.info(`✅ Generated favicon: ${name} (${size}x${size})`) } // 生成主 favicon.ico(使用 32x32) - const mainFaviconSvg = createFaviconSVG(32) - const faviconBuffer = await sharp(Buffer.from(mainFaviconSvg)) - .png() + const faviconResizedBuffer = await sharp(logoBuffer) + .resize(32, 32, { + fit: 'cover', + position: 'center', + }) + .png({ + quality: 100, + compressionLevel: 6, + }) .toBuffer() + // 为 favicon.ico 添加圆角 + const faviconBuffer = await addRoundedCorners(faviconResizedBuffer, 32) + const faviconPath = join(outputDir, 'favicon.ico') writeFileSync(faviconPath, faviconBuffer) console.info(`✅ Generated main favicon: favicon.ico`) @@ -134,7 +129,9 @@ export async function generateFavicons() { writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)) console.info(`✅ Generated web manifest: site.webmanifest`) - console.info(`🎨 All favicons generated successfully!`) + console.info( + `🎨 All favicons generated successfully from logo.jpg with rounded corners!`, + ) } catch (error) { console.error('❌ Error generating favicons:', error) throw error