macOS 菜单栏小工具:让「双击窗口标题栏放大 / 还原」的系统动画期间,窗口内容也保持顺滑, 同时完全保留系统原有动画时长与曲线。
适用于 Chromium / Electron 系应用(Edge、Chrome、DeepSeek Harness、ZCode、VS Code、Obsidian…) 等"缩放时内容会重新排版"的窗口。
Chromium / Electron 应用在窗口放大 / 还原动画进行时,会实时重新排版网页内容。重排是异步的, 通常跟不上窗口边框的运动,于是你看到的是:窗口边框很丝滑,里面的内容却一格一格地跳、慢半拍。 原生 macOS 应用在缩放期间通常直接拉伸上一帧画面,所以看起来是顺的。
SmoothZoom 的做法是把系统动画「接管显示」:
- 双击标题栏时,截取窗口快照与屏幕背景快照(仅内存,不落盘);
- 检测到系统 zoom 动画开始后,显示一个全屏 overlay:冻结背景 + 冻结窗口内容, 窗口面 / 内容边缘按窗口实时 frame 平滑变化;
- 动画结束时撤掉 overlay,露出已经重新排版完成的清晰原生内容。
动画的时长、曲线、缓动全部沿用系统原生动画(overlay 只是逐帧跟随窗口 frame),
所以观感与原生一致——它不是改 NSWindowResizeTime / 关动画那种"变快变生硬"的方案。
- 系统级、全局:对任意 App 的普通窗口生效(全屏窗口自动跳过)
- 保留系统原始动画时长与曲线(不修改任何系统设置)
- 三种内容策略可在菜单栏实时切换(见下文「缩放内容效果」)
- 双击瞬间无感预盖:内容不再出现"开始前一刻的偏移 / 跳帧"
- 支持带透明 / 毛玻璃 / 半透明侧边栏的 UI(截取屏幕合成外观,而非仅窗口位图)
- 仅需 屏幕录制 权限;不需要辅助功能(Accessibility)或输入监控
- 截图只在内存中,不写盘、不上传
- macOS 14 或更新版本(在 macOS 26 / Apple Silicon 上开发验证)
- 屏幕录制权限(唯一需要的权限)
./build.sh # 编译 -> build/SmoothZoom.app
./install.sh # 安装到 /Applications 并启动首次启动会提示「屏幕录制」权限:系统设置 → 隐私与安全性 → 屏幕录制 → 勾选 SmoothZoom → 退出并重新打开 SmoothZoom(macOS 的屏幕录制授权需要重启 App 才生效)。
build.sh/install.sh会优先使用本机已有的代码签名身份,找不到时退回 ad-hoc 签名。 使用 ad-hoc 签名的副作用:每次重新编译后系统可能要求重新授权屏幕录制。 如果希望"升级不再要求授权",可以自建一个稳定的代码签名证书(TCC 授权绑定证书,而非二进制哈希)。
- 双击任意窗口标题栏即可放大 / 还原;
- 点击菜单栏图标:
- 启用平滑缩放 — 全局开关
- 只处理网页类 App(Chromium / Electron) — 更省资源
- 缩放内容效果 — 切换三种内容策略(见下)
- 登录时自动启动、写入调试日志、屏幕录制权限、打开日志文件夹、关于、退出
- 日志:
~/Library/Logs/SmoothZoom/smoothzoom.log
| 模式 | 行为 |
|---|---|
| ① 位图缩放跟随 | 逐帧跟随窗口 frame,内容位图跟随边框缩放(最早版本) |
| ② 合成缩放(CA 驱动) | 由 Core Animation 驱动整段动画,内容位图平滑缩放(类绿色按钮过渡) |
| ③ 内容保持不缩放,结束后加载(默认) | 动画期间旧内容保持可见、不缩放也不移动;扩展区域用窗口底色填充;结束后淡入真实内容 |
对应 defaults:contentMode(0 / 1 / 2)。
# 内容模式:0=位图跟随 1=合成缩放 2=保持不缩放+结束后加载(默认)
defaults write com.halo.smoothzoom contentMode -int 2
# 模式③:结束淡入时长(毫秒,0=硬切)
defaults write com.halo.smoothzoom holdFadeMs -float 160
# 模式③:内容 / 窗口边缘羽化开关与圆角
defaults write com.halo.smoothzoom feather -bool true
defaults write com.halo.smoothzoom cornerRadius -float 10
# 模式③:逐帧跟随真实窗口动画(默认);0 = 旧版合成动画
defaults write com.halo.smoothzoom trackNative -int 1
# 双击预盖(放大方向,避免开始前的内容偏移)
defaults write com.halo.smoothzoom preshow -int 1
# 合成动画时长(模式①②)
defaults write com.halo.smoothzoom syntheticMs -float 420
# 位图放大时的清晰度增强(GPU Lanczos + unsharp)
defaults write com.halo.smoothzoom enhance -bool true
defaults write com.halo.smoothzoom enhanceScale -float 2.4
defaults write com.halo.smoothzoom enhanceSharpness -float 0.55
# 调试
defaults write com.halo.smoothzoom debugLogging -bool true- 全屏窗口(绿色按钮进入的系统全屏)不处理;原生全屏切换有自己的动画。
- 系统开启「减弱动态效果」时不处理。
- 应用自定义了标题栏双击行为(不做系统 zoom)时不会触发。
- 模式③在缩回方向偶发一次约 1 帧的瞬闪(系统收缩启动瞬间的窗口帧报告时序问题), 概率约 3/10,尚未完全消除;介意可只用放大方向或切换到模式 ②。
| 模块 | 方案 |
|---|---|
| 全局双击检测 | NSEvent.addGlobalMonitorForEvents(鼠标事件无需 TCC 权限,自带 clickCount,不吞事件) |
| 窗口快照 | CGWindowListCreateImage(dlsym 获取;macOS 15+ 头文件标记 obsolete,但运行时仍可用,约 11–20 ms) |
| 背景快照 | CGWindowListCreateImage(.optionOnScreenBelowWindow)(约 15–40 ms,首击预取缓存) |
| 窗口 frame 轮询 | 私有 SkyLight SLSGetWindowBounds(约 10 µs / 次,180 Hz),失败回退 CGWindowListCopyWindowInfo |
| 覆盖层 | 全屏透明 NSWindow(.floating、ignoresMouseEvents、isOpaque=false、canJoinAllSpaces/fullScreenAuxiliary)+ CALayer |
| 动画 | 逐帧跟随系统动画本身,所以时长/曲线与原生完全一致;结束以 frame 连续 3 帧稳定判定 |
smoothzoom/
├── Sources/ # Swift 源码(菜单栏 App,无第三方依赖)
├── Resources/ # Info.plist / AppIcon
├── build.sh # 编译
├── devbuild.sh # 开发用构建(独立 bundle id,便于本地迭代)
├── install.sh # 安装到 /Applications
└── README.md
MIT © 2026 Halo0sama
本项目属于系统级视觉增强实验:通过外部 overlay 复现系统缩放动画观感,不修改任何系统设置、 不注入任何进程。使用风险自负。