演讲超时?课堂拖堂?用一个赛博朋克风格的桌面倒计时器,帮你精准掌控每一分钟。
痛点
讲PPT的时候,你大概率遇到过这些场景:
- 答辩给了8分钟,讲到一半才发现已经过了6分钟
- 手机计时器被PPT全屏挡住,根本看不到
- Windows自带的闹钟在全屏模式下不弹出
- 市面上的计时器软件要么丑得离谱,要么不能真正置顶在PPT放映模式之上
核心矛盾很简单:PowerPoint的放映模式会独占全屏,普通窗口根本显示不出来。
PPT Timer 就是为了解决这一个问题而生的。
它长什么样
黑底荧光绿,DSEG7液晶字体,模拟真实LCD面板------上世纪终端机和考试用电子钟的既视感:

┌──────────────────────────┐
│ TIMER × │
│ ┌──────────────────────┐ │
│ │ 05:00 │ │ ← DSEG7 七段液晶数字 + 荧光绿发光
│ │ (88:88) │ │ ← 底层灰暗底纹,模拟未点亮的LCD段
│ └──────────────────────┘ │
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │ ← 30段LED进度条
│ ▶ ↺ │ ← 播放/暂停 + 重置
│ 01 03 05 08 15 + │ ← 预设按钮(分钟)
└──────────────────────────┘
↑ 1px荧光绿边框,终端风
核心特性
真正的置顶 ------ 使用 Electron 的 screen-saver 级别窗口层级,比系统级别的 alwaysOnTop 更高一层。PPT放映模式、游戏全屏、视频播放器------都挡不住它。
javascript
win.setAlwaysOnTop(true, 'screen-saver');
这一行是整个项目的灵魂。Electron 提供了多个置顶级别(normal → floating → torn-off-menu → modal-panel → main-menu → status → pop-up-menu → screen-saver),screen-saver 是最高级别,连屏保都压得住。
60fps 流畅计时 ------ 没有用 setInterval。setInterval 在后台标签页或高负载时会被浏览器降频,导致倒计时不准。PPT Timer 使用 requestAnimationFrame + delta time 差值计算:
javascript
function tick(timestamp) {
const delta = timestamp - lastTick;
lastTick = timestamp;
remainingMs -= delta;
// ...
requestAnimationFrame(tick);
}
计时精度不依赖定时器的调用频率,而是依赖真实流逝的时间。
颜色渐变预警 ------ 倒计时不是只有数字在跳。整个界面会根据剩余时间改变状态:
| 剩余时间 | 颜色 | 效果 |
|---|---|---|
| > 30s | #00ff41 荧光绿 |
正常计时 |
| 10~30s | #ffb000 琥珀黄 |
边框 + LED条变黄 |
| < 10s | #ff0040 红色 |
数字闪烁 + LED条闪动 |
| 0s | 红色 | 全窗口闪烁3次 |
不需要盯着数字看------余光瞟到颜色变了就知道该收尾了。
技术实现
项目结构
bash
ppt-timer/
├── main.js # Electron 主进程:窗口配置 + 置顶策略
├── preload.js # 安全桥接:contextIsolation + IPC
├── renderer/
│ ├── index.html # 界面布局
│ ├── style.css # LCD主题 + 动画 + DSEG字体
│ └── app.js # 计时引擎 + 状态机
├── assets/
│ └── icon.ico # 七段LCD风格图标
├── .github/workflows/
│ └── release.yml # CI:tag推送自动构建发布
└── package.json
总共不到 600 行代码,零运行时依赖。纯 HTML/CSS/JS,没有 React,没有 Vue,没有 Tailwind。
窗口策略
无边框 + 透明背景 + 不可缩放,窗口本身是"隐形"的,只有中间那个280x320的黑色面板是可见的:
javascript
new BrowserWindow({
width: 280, height: 320,
frame: false, // 去掉标题栏
transparent: true, // 背景透明
resizable: false,
alwaysOnTop: true
});
拖拽通过 CSS 实现------标题栏区域设置 -webkit-app-region: drag,按钮区域设置 no-drag。
窗口失焦时会重新声明置顶级别,防止某些场景下被其他窗口抢占:
javascript
win.on('blur', () => {
win.setAlwaysOnTop(true, 'screen-saver');
});
LCD 视觉效果
DSEG7 字体 ------ 开源的七段显示字体,通过 jsDelivr CDN 加载。底层叠了一层暗灰色的 88:88 作为LCD底纹,模拟真实液晶屏上未点亮的笔段。
Scanline 扫描线 ------ 用 CSS repeating-linear-gradient 叠加2px间距的半透明横线,模拟CRT/LCD的扫描线质感。
Glow 发光 ------ text-shadow 双层扩散,内层8px紧贴,外层20px弥散,模拟荧光管的发光晕染。
状态机
计时器有4个状态,转换路径明确:
arduino
idle → running → paused → running → ... → finished
↑ │
└──────────── reset ←──────────────────────┘
LED 进度条
不是圆环,不是线性进度条------是 30个独立的小方块,逐段熄灭,像真实的LED阵列指示灯。最后10秒末尾的方块会高频闪动。
使用方式
下载安装
直接去 GitHub Releases 下载 PPT Timer Setup 0.1.0.exe,双击安装。
从源码运行
bash
git clone https://github.com/ZengLiangYi/ppt-timer.git
cd ppt-timer
npm install
npm start
需要 Node.js 20+。
操作
- 点击底部预设按钮(
0103050815)直接开始倒计时 - 点击
+输入自定义分钟数 ▶暂停/继续,↺重置- 拖拽标题栏移动窗口位置
- 打开PPT放映 → 计时器始终在最上层
CI/CD:推 Tag 自动发布
项目配置了 GitHub Actions,工作流程:
perl
git tag v0.2.0
git push origin v0.2.0
推送 v* 格式的 tag 后,GitHub Actions 自动在 windows-latest 上执行 npm ci → electron-builder --win → 将生成的 .exe 安装包发布到 GitHub Releases。
整个流水线大约2分钟跑完。不需要本地装打包环境,不需要手动上传文件。
已知限制
- 目前只打包了 Windows x64 版本(PPT放映主要是Windows场景)
- 字体通过CDN加载,首次启动需要网络(后续有缓存)
- 没有声音提醒(考虑到演讲场景,声音反而是干扰)
后续计划
- 字体本地化打包,支持离线使用
- macOS 打包支持
- 可配置的预设时间组
- 正计时模式
- 多显示器场景优化
GitHub : github.com/ZengLiangYi...
下载 : PPT Timer v0.1.0
协议: MIT --- 随便用,随便改。