如何在macOS版Electron中为Template Image叠加红色徽章作菜单栏图标?
Electron Template Image叠加红色徽章实现方案
核心问题在于:Template Image会自动过滤所有颜色信息,只保留明暗轮廓,所以直接把带红色徽章的图转为Template会丢失徽章颜色。正确的做法是将基础图标设为Template,再单独叠加彩色徽章,具体有两种实现方式:
方式一:用Canvas动态绘制徽章(适合动态数字徽章)
需要先安装canvas依赖包:npm install canvas
const { app, Menu, nativeImage } = require('electron'); const { createCanvas, loadImage } = require('canvas'); async function getBadgedTemplateIcon(baseIconPath, badgeNum) { // 加载基础模板图标并标记为Template const baseTemplate = nativeImage.createFromPath(baseIconPath).setTemplateImage(true); const baseImg = await loadImage(baseTemplate.toDataURL()); // 创建与基础图标尺寸一致的画布 const canvas = createCanvas(baseImg.width, baseImg.height); const ctx = canvas.getContext('2d'); // 绘制基础模板图标 ctx.drawImage(baseImg, 0, 0); // 绘制红色圆形徽章(右上角位置) const badgeSize = Math.floor(baseImg.width * 0.3); const posX = baseImg.width - badgeSize - 5; const posY = 5; // 徽章背景 ctx.fillStyle = '#ff3b30'; ctx.beginPath(); ctx.arc(posX + badgeSize/2, posY + badgeSize/2, badgeSize/2, 0, Math.PI * 2); ctx.fill(); // 徽章数字 ctx.fillStyle = '#ffffff'; ctx.font = `${badgeSize * 0.6}px sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(badgeNum.toString(), posX + badgeSize/2, posY + badgeSize/2); // 转为Electron NativeImage返回 return nativeImage.createFromDataURL(canvas.toDataURL()); } // 应用启动后生成带徽章的图标并设置菜单 app.whenReady().then(async () => { const menuIcon = await getBadgedTemplateIcon('./base-template-icon.png', 3); const appMenu = Menu.buildFromTemplate([ { label: '我的应用', icon: menuIcon, submenu: [{ role: 'quit' }] } ]); Menu.setApplicationMenu(appMenu); });
方式二:预生成徽章图叠加(适合静态徽章)
如果徽章是固定样式,无需动态修改数字,可以提前做好红色徽章图片,直接用NativeImage的composite方法叠加:
const { app, Menu, nativeImage } = require('electron'); app.whenReady().then(() => { // 加载基础模板图标并标记为Template const baseTemplate = nativeImage.createFromPath('./base-template-icon.png').setTemplateImage(true); // 加载预制作的红色徽章图(普通彩色图,不要设为Template) const redBadge = nativeImage.createFromPath('./red-badge.png'); // 计算徽章位置(右上角偏移5px) const baseSize = baseTemplate.getSize(); const badgeSize = redBadge.getSize(); const badgePos = { x: baseSize.width - badgeSize.width - 5, y: 5 }; // 合成图标 const badgedIcon = baseTemplate.composite(redBadge, badgePos); // 设置菜单栏 const appMenu = Menu.buildFromTemplate([ { label: '我的应用', icon: badgedIcon, submenu: [{ role: 'quit' }] } ]); Menu.setApplicationMenu(appMenu); });
关键注意点
- 基础图标必须是纯黑白轮廓图,标记为Template后,系统会根据菜单栏的明暗主题自动适配图标颜色。
- 徽章部分不能标记为Template,要保留原始颜色,叠加后不会被Template的颜色过滤规则影响。
内容的提问来源于stack exchange,提问作者Xue An Chuang
相关产品推荐
相关产品推荐

