平台 图标尺寸规范 交互特性 上下文菜单位置 Windows 16x16 ~ 24x24 像素 左键单击触发主事件 图标正下方 macOS 22x22 像素(Retina) 强制右键显示菜单 跟随光标位置 Linux 24x24 像素 依赖桌面环境实现 各发行版差异较大 
 
Electron 15+ 版本的核心改进:
 
高分辨率图标支持(SVG/多倍图) 动态菜单项更新(无需重建整个菜单) 托盘气泡通知的增强API 深色模式自动适配 
import  {  Tray,  Menu,  nativeImage }  from  'electron' 
const  trayIcon =  nativeImage. createFromPath ( 'icon.png' ) . resize ( {  width:  16 ,  height:  16  } ) 
const  appTray =  new  Tray ( trayIcon) 
const  contextMenu =  Menu. buildFromTemplate ( [ 
  {  label:  '显示主窗口' ,  click :  ( )  =>  mainWindow. show ( )  } , 
  {  type :  'separator'  } , 
  {  label:  '退出' ,  role:  'quit'  } 
] ) 
appTray. setContextMenu ( contextMenu) 
function  updateTrayIcon ( usage:  number )  { 
  const  canvas =  createCanvas ( 16 ,  16 ) 
  const  ctx =  canvas. getContext ( '2d' ) 
  
  
  ctx. beginPath ( ) 
  ctx. arc ( 8 ,  8 ,  7 ,  0 ,  2  *  Math. PI  *  usage) 
  ctx. strokeStyle =  '#4CAF50' 
  ctx. lineWidth =  2 
  ctx. stroke ( ) 
  appTray. setImage ( canvas. toDataURL ( ) ) 
} 
setInterval ( ( )  =>  { 
  const  usage =  getCPUUsage ( )  
  updateTrayIcon ( usage) 
} ,  1000 ) 
class  TrayAnimator  { 
  private  frames:  NativeImage[ ]  =  [ ] 
  private  currentFrame =  0 
  constructor ( framesPath:  string [ ] )  { 
    this . frames =  framesPath. map ( p =>  nativeImage. createFromPath ( p) ) 
  } 
  start ( )  { 
    setInterval ( ( )  =>  { 
      appTray. setImage ( this . frames[ this . currentFrame] ) 
      this . currentFrame =  ( this . currentFrame +  1 )  %  this . frames. length
    } ,  100 ) 
  } 
} 
const  animator =  new  TrayAnimator ( [ 'frame1.png' ,  'frame2.png' ,  'frame3.png' ] ) 
animator. start ( ) 
appTray
  . on ( 'click' ,  ( event,  bounds)  =>  handleTrayClick ( event,  bounds) ) 
  . on ( 'double-click' ,  ( )  =>  toggleMainWindow ( ) ) 
  . on ( 'right-click' ,  ( )  =>  showContextMenu ( ) ) 
  . on ( 'drop-files' ,  ( _,  files)  =>  handleFileDrop ( files) ) 
appTray. setIgnoreDoubleClickEvents ( true ) 
appTray. on ( 'drop-files' ,  ( event,  files)  =>  { 
  if  ( files. some ( f =>  f. endsWith ( '.txt' ) ) )  { 
    processTextFiles ( files) 
  } 
} ) 
捕捉模式 API 适用场景 性能影响 全屏静态截图 desktopCapturer.getSources 快速截图 低 窗口实时流 navigator.mediaDevices.getUserMedia 录屏/直播 中 指定区域捕捉 结合DOM元素与流媒体 区域录制 高 
 
import  {  desktopCapturer }  from  'electron' 
async  function  captureFullScreen ( )  { 
  const  sources =  await  desktopCapturer. getSources ( { 
    types:  [ 'screen' ] , 
    thumbnailSize:  {  width:  1920 ,  height:  1080  } 
  } ) 
  const  primaryScreen =  sources. find ( s =>  s. name ===  'Entire Screen' ) 
  if  ( primaryScreen)  { 
    fs. writeFileSync ( 'screenshot.png' ,  primaryScreen. thumbnail. toPNG ( ) ) 
  } 
} 
const  captureOptions =  { 
  types:  [ 'screen' ] , 
  thumbnailSize:  {  
    width:  screen. getPrimaryDisplay ( ) . size. width, 
    height:  screen. getPrimaryDisplay ( ) . size. height
  } 
} 
desktopCapturer. getSources ( captureOptions) . then ( sources =>  { 
  sources. forEach ( ( source,  index)  =>  { 
    fs. writeFileSync ( ` screen_ ${ index} .png ` ,  source. thumbnail. toPNG ( ) ) 
  } ) 
} ) 
async  function  startCapture ( )  { 
  const  constraints =  { 
    audio:  false , 
    video:  { 
      mandatory:  { 
        chromeMediaSource:  'desktop' , 
        chromeMediaSourceId:  await  getSourceId ( ) 
      } 
    } 
  } 
  const  stream =  await  navigator. mediaDevices. getUserMedia ( constraints) 
  const  videoElement =  document. getElementById ( 'preview' ) 
  videoElement. srcObject =  stream
} 
async  function  getSourceId ( )  { 
  const  sources =  await  desktopCapturer. getSources ( {  types:  [ 'window' ,  'screen' ]  } ) 
  return  sources[ 0 ] . id
} 
const  video =  document. querySelector ( 'video' ) 
const  canvas =  document. createElement ( 'canvas' ) 
const  ctx =  canvas. getContext ( '2d' ) 
video. addEventListener ( 'play' ,  ( )  =>  { 
  function  processFrame ( )  { 
    canvas. width =  video. videoWidth
    canvas. height =  video. videoHeight
    ctx. drawImage ( video,  0 ,  0 ) 
    
    const  imageData =  ctx. getImageData ( 0 ,  0 ,  canvas. width,  canvas. height) 
    
    
    if  ( ! video. paused)  requestAnimationFrame ( processFrame) 
  } 
  requestAnimationFrame ( processFrame) 
} ) 
const  chunks:  Blob[ ]  =  [ ] 
const  mediaRecorder =  new  MediaRecorder ( stream,  { 
  mimeType:  'video/webm; codecs=vp9' , 
  bitsPerSecond:  2500000 
} ) 
mediaRecorder. ondataavailable  =  ( e)  =>  { 
  chunks. push ( e. data) 
} 
mediaRecorder. onstop  =  ( )  =>  { 
  const  blob =  new  Blob ( chunks,  {  type :  'video/webm'  } ) 
  saveFile ( blob) 
} 
appTray. on ( 'click' ,  ( )  =>  { 
  if  ( mediaRecorder. state ===  'recording' )  { 
    mediaRecorder. stop ( ) 
  }  else  { 
    mediaRecorder. start ( 1000 )  
  } 
} ) 
 
  
   
   
    
     
      
       
        
          
        
       
      
       
        
          
        
       
      
       
        
          
        
       
      
       
        
          
        
       
      
       
        
          
        
       
      
       
        
          
        
       
      
       
        
          
        
       
      
     
      
       
        
         
         
 
         
        
       
      
       
        
         
         区域选择  
        
 
         
        
       
      
       
        
         
         全屏截图  
        
 
         
        
       
      
       
        
         
         
 
         
        
       
      
       
        
         
         
 
         
        
       
      
       
        
         
         
 
         
        
       
      
       
        
         
         
 
         
        
       
      
     
      
       
        
         
         
           托盘点击事件 
         
 
          
         
        
       
      
       
        
         
         
           模式判断 
         
 
          
         
        
       
      
       
        
         
         
           激活DOM遮罩层 
         
 
          
         
        
       
      
       
        
         
         
           调用desktopCapturer 
         
 
          
         
        
       
      
       
        
         
         
           鼠标事件监听 
         
 
          
         
        
       
      
       
        
         
         
           计算选择区域 
         
 
          
         
        
       
      
       
        
         
         
           像素数据裁剪 
         
 
          
         
        
       
      
       
        
         
         
           保存/编辑图片 
         
 
          
         
        
       
      
     
    
   
  
优化方向 实现方案 效果提升 内存优化 使用SharedArrayBuffer 减少30%内存占用 CPU占用优化 动态调整捕捉帧率 降低40%CPU使用 存储优化 实时压缩算法 节省50%磁盘空间 
 
ipcMain. handle ( 'request-capture' ,  ( event)  =>  { 
  if  ( checkPermission ( 'screen-capture' ) )  { 
    return  desktopCapturer. getSources ( options) 
  }  else  { 
    dialog. showErrorBox ( '权限不足' ,  '需要屏幕捕捉权限' ) 
    throw  new  Error ( 'Permission denied' ) 
  } 
} ) 
function  encryptScreenshot ( data:  Buffer)  { 
  const  iv =  crypto. randomBytes ( 16 ) 
  const  cipher =  crypto. createCipheriv ( 'aes-256-cbc' ,  SECRET_KEY ,  iv) 
  return  Buffer. concat ( [ iv,  cipher. update ( data) ,  cipher. final ( ) ] ) 
} 
function  checkScreenCapturePermission ( )  { 
  if  ( process. platform !==  'darwin' )  return  true 
  
  const  status =  systemPreferences. getMediaAccessStatus ( 'screen' ) 
  return  status ===  'granted' 
} 
if  ( ! checkScreenCapturePermission ( ) )  { 
  systemPreferences. askForMediaAccess ( 'screen' ) 
} 
桌面环境 兼容方案 已知问题 GNOME 使用org.gnome.Shell接口 需要用户手动授权 KDE Plasma 通过DBus接口调用 多显示器支持不完善 Xfce 依赖xrandr工具 无法获取窗口标题 
 
describe ( '屏幕捕捉测试' ,  ( )  =>  { 
  let  window:  BrowserWindow
  before ( ( )  =>  { 
    window =  new  BrowserWindow ( {  show:  false  } ) 
  } ) 
  it ( '应成功获取屏幕源' ,  async  ( )  =>  { 
    const  sources =  await  desktopCapturer. getSources ( {  types:  [ 'screen' ]  } ) 
    assert ( sources. length >  0 ) 
  } ) 
} ) 
const  perfHook =  require ( 'electron-performance-hook' ) 
perfHook. monitor ( 'desktopCapturer' ,  { 
  onStart :  ( id)  =>  console . time ( ` capture- ${ id} ` ) , 
  onEnd :  ( id)  =>  console . timeEnd ( ` capture- ${ id} ` ) 
} ) 
通过本文的深度实践,开发者可以掌握:
 
托盘功能的精细化控制 高性能屏幕捕捉方案 企业级录屏工具架构 跨平台兼容性解决方案 建议进一步探索:
 
GPU加速渲染优化 基于WebCodecs的底层编解码 与AI模型的集成(如实时物体识别)