Tauri 窗口拖拽每隔几秒才动一下:极光背景的大号 blur 动画在饿死原生拖拽
给桌面 APP 做了一套极光玻璃感界面——Aurora 光晕在背景缓慢漂移,上面叠着几层 backdrop-filter 毛玻璃面板,效果相当好看。但有一天发现:拖拽窗口的时候,窗口动一下,然后冻住两秒,再跳一下。像在用幻灯片拖窗口。
第一反应是 data-tauri-drag-region 绑得不对,或者 JS 事件处理器太重。排查半天,处理器根本没问题,甚至主线程都是空闲的。
现象
- 自定义标题栏使用
data-tauri-drag-region(或 Electron 的-webkit-app-region: drag) - 拖拽时窗口不跟手,每隔 1~2 秒才挪一次,感觉被"节流"了
- 只要页面里有持续动画的大面积极光背景,必现;页面静止时偶尔好一点
根因
filter: blur(r) 本质上是一次 Gaussian 模糊光栅化。如果元素静止不动,浏览器只光栅化一次,结果缓存为合成层位图——之后每帧几乎零开销。
但如果你让这个已经模糊的元素持续动画(@keyframes 驱动 transform: translate/scale/rotate),情况完全不同:元素的像素/位置每帧都在变,合成器必须每帧重新光栅化 + 重新 blur。对一个 50vw 的元素加 blur(80px) 跑 60fps,这是持续的巨量 GPU 负载。
Tauri/Electron 的原生窗口拖拽底层依赖 WebView 的合成器管道——操作系统每帧都要 WebView 报告最新的窗口位置,而合成器正在全力跑模糊,根本抢不到时间片,拖拽就被**饿死(starved)**了。
filter: blur() 元素一旦被持续动画驱动(哪怕只是缓慢漂移),合成器就得每帧重新光栅化 + 重新模糊。Tauri/Electron 的原生窗口拖拽与这条管道共用时间片,满载时必然被饿死——表现就是窗口每隔几秒才跳一次。
变体 B:静态 backdrop-filter 叠在实时内容上
第二种症状稍微隐蔽:极光背景没有动画,拖拽大部分时候正常,但 APP 里有终端/视频/Canvas 在刷新输出时偶尔卡顿。
根因是:一个静止的 backdrop-filter 面板覆盖在持续重绘的内容(流式输出的终端、WebGL canvas)上方——backdrop-filter 必须采样背后的内容,背后每帧都在变,blur 就每帧都要重新算。本质和变体 A 一样,只是触发源不同。
更无解的是:原生窗口拖拽不触发任何 JS 事件,你无法在 mousedown 时切换 backdrop-filter: none 来规避——只能从根本上把高代价的 blur 挪离实时内容。
正解
让大 blur 元素彻底静止,把连续动画干掉:
/* BEFORE — 每帧重新光栅化 blur(80px),拖拽必卡 */
.aurora-blob {
filter: blur(80px);
will-change: transform;
animation: drift 22s ease-in-out infinite;
}
/* AFTER — blur 光栅化一次后缓存,合成器解放 */
.aurora-blob {
filter: blur(80px);
transform: scale(1.15); /* 静态摆好姿势,不再动 */
}极光的美感几乎不受影响(原本就是 22s 超慢漂移)。拖拽立刻流畅。
如果非要保留动感,只能走不触发重光栅化的路:仅动 opacity(compositor-only)、缩小 blur 半径、缩小元素尺寸。
针对变体 B,把叠在实时内容上的 backdrop-filter 替换为纯半透明实色背景:
/* BEFORE — 终端刷输出时每帧重 blur */
.term-toolbar {
backdrop-filter: blur(12px) saturate(140%);
}
/* AFTER — 视觉几乎一样,compositor 解放 */
.term-toolbar {
background: rgba(16, 24, 36, 0.92);
}极光光晕去掉连续动画,保持静态姿势——blur 只光栅化一次,之后零开销。叠在终端 / Canvas 上的毛玻璃面板改用纯半透明实色背景。两条规则同时满足,原生窗口拖拽重新得到完整时间片。
验证方法
- 打开 DevTools Performance,录制拖拽过程——修复前 GPU/compositor 长期满载,修复后空闲
getComputedStyle(blob).animationName === 'none'确认动画已移除- 拖拽窗口横穿屏幕,全程跟手无跳帧
一句话外卖
桌面 WebView 的原生窗口拖拽靠合成器管道活着——任何让合成器每帧重新光栅化的操作(大 blur 连续动画 / backdrop-filter 叠实时内容)都会把拖拽饿死;治本的方法只有让大 blur 彻底静止。