在Angular与Electron结合的桌面应用开发中,实现应用级闲置屏幕保护需要同时处理前端交互监听、进程通信、全屏页面展示三个核心环节,整体方案需要兼顾渲染进程的用户行为捕获和主进程的全屏控制能力。

核心实现思路
整个功能的实现分为三个步骤:首先在Angular渲染进程中监听用户的鼠标移动、键盘按下、点击等交互事件,计算用户最后一次操作的时间;其次当闲置时长达到预设阈值时,通过Electron的IPC通信通知主进程;最后主进程创建全屏的屏幕保护窗口,用户重新交互时关闭保护窗口回到主应用。
渲染进程闲置检测实现
在Angular服务中封装闲置检测逻辑,监听全局的用户交互事件,实时更新最后操作时间戳,定时检查闲置时长。
创建闲置检测服务
在Angular项目中新建idle-detector.service.ts文件,实现如下逻辑:
import { Injectable, NgZone } from '@angular/core';
import { ipcRenderer } from 'electron';
@Injectable({
providedIn: 'root'
})
export class IdleDetectorService {
// 预设闲置时间,单位毫秒,这里设置为5分钟
private idleThreshold = 5 * 60 * 1000;
// 最后操作时间戳
private lastActiveTime: number = Date.now();
// 检测定时器
private checkTimer: any;
// 是否正在显示屏幕保护
private isScreenProtecting = false;
constructor(private ngZone: NgZone) {
// 确保ipcRenderer在Electron环境下可用
if (window && (window as any).require) {
this.ipcRenderer = (window as any).require('electron').ipcRenderer;
}
}
/**
* 启动闲置检测
*/
startDetect(): void {
this.bindUserEvents();
this.startCheckTimer();
}
/**
* 绑定用户交互事件
*/
private bindUserEvents(): void {
const events = ['mousemove', 'keydown', 'click', 'scroll', 'touchstart'];
events.forEach(eventName => {
document.addEventListener(eventName, () => {
this.updateLastActiveTime();
// 如果正在显示屏幕保护,通知主进程关闭
if (this.isScreenProtecting) {
this.ipcRenderer.send('close-screen-protect');
this.isScreenProtecting = false;
}
});
});
}
/**
* 更新最后操作时间
*/
private updateLastActiveTime(): void {
this.lastActiveTime = Date.now();
}
/**
* 启动定时检查
*/
private startCheckTimer(): void {
// 使用NgZone确保定时器在Angular的变更检测范围内
this.ngZone.runOutsideAngular(() => {
this.checkTimer = setInterval(() => {
const idleTime = Date.now() - this.lastActiveTime;
if (idleTime >= this.idleThreshold && !this.isScreenProtecting) {
this.isScreenProtecting = true;
// 通知主进程显示屏幕保护
this.ipcRenderer.send('show-screen-protect');
}
}, 1000);
});
}
/**
* 停止检测
*/
stopDetect(): void {
if (this.checkTimer) {
clearInterval(this.checkTimer);
}
}
}
在App模块中引入服务
在app.module.ts中引入该服务,并在应用启动时调用检测方法:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { IdleDetectorService } from './idle-detector.service';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [IdleDetectorService],
bootstrap: [AppComponent]
})
export class AppModule {}
在app.component.ts的ngOnInit中启动检测:
import { Component, OnInit } from '@angular/core';
import { IdleDetectorService } from './idle-detector.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
constructor(private idleDetector: IdleDetectorService) {}
ngOnInit(): void {
this.idleDetector.startDetect();
}
}
Electron主进程逻辑实现
在主进程文件(通常是main.js或main.ts)中处理渲染进程的IPC消息,控制屏幕保护窗口的显示与关闭。
创建屏幕保护窗口
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
let mainWindow;
let screenProtectWindow = null;
// 创建主窗口
function createMainWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true
}
});
mainWindow.loadURL('http://localhost:4200');
}
// 创建屏幕保护窗口
function createScreenProtectWindow() {
screenProtectWindow = new BrowserWindow({
fullscreen: true,
frame: false,
alwaysOnTop: true,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true
}
});
// 加载屏幕保护页面,这里可以指向Angular的路由页面,或者单独的html文件
screenProtectWindow.loadURL('http://localhost:4200/screen-protect');
// 禁止屏幕保护窗口被关闭
screenProtectWindow.on('close', (e) => {
e.preventDefault();
});
}
// 监听显示屏幕保护消息
ipcMain.on('show-screen-protect', () => {
if (!screenProtectWindow) {
createScreenProtectWindow();
}
});
// 监听关闭屏幕保护消息
ipcMain.on('close-screen-protect', () => {
if (screenProtectWindow) {
screenProtectWindow.close();
screenProtectWindow = null;
// 将主窗口置顶
if (mainWindow) {
mainWindow.focus();
}
}
});
app.whenReady().then(() => {
createMainWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createMainWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
屏幕保护页面实现
在Angular中新增屏幕保护路由页面,展示全屏的保护内容,比如时钟、提示信息等,页面不需要复杂交互,仅用于展示。
<div class="screen-protect-container">
<h1>应用闲置中</h1>
<p>请移动鼠标或按下键盘回到应用</p>
<div class="time">{{ currentTime }}</div>
</div>
对应的样式文件:
.screen-protect-container {
width: 100vw;
height: 100vh;
background: #000;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 2rem;
}
.time {
margin-top: 2rem;
font-size: 4rem;
}
注意事项
- 如果应用需要排除某些页面的闲置检测,可以在服务中增加页面路由判断逻辑,在指定路由下暂停检测
- 屏幕保护窗口的
alwaysOnTop属性需要设置为true,避免被其他窗口遮挡 - 生产环境下需要替换
loadURL的地址为打包后的本地文件路径,确保窗口能正常加载 - 如果用户有外接设备,需要额外监听外接设备的交互事件,避免误判闲置状态