本帖最后由 cyfung1031 于 2026-2-25 14:23 编辑
本内容取自 2023年 Jane Ori 所编写的 Expert CSS: The CPU Hack
专家级 CSS:CPU Hack
“CPU Hack” 的含义是解锁 CSS 在不依赖 JavaScript 的情况下持续处理数据并重新评估状态的能力。
例如,如果循环变量在 CSS 中不会自动回到无效 (initial) 状态,那么下面的代码会持续递增 --frame-count 的值:
body {
--input-frame: var(--frame-count, 0);
--frame-count: calc(var(--input-frame) + 1);
}
剧透:实际上你可以仅用 CSS 实现这一点,而不需要任何 JS,我会在下文展示如何做到!
🧠 5 个可观察现象(The 5 Observables)
在正式演示之前,我们先了解 CSS 动画的一些高级行为,这样最终演示不会显得太突兀。
1. 动画状态优先于所有选择器状态(几乎总是)
动画状态中定义的属性会覆盖所有选择器状态中的属性。例如:
body {
animation: example 1s infinite;
--color: blue;
background: var(--color);
}
body:hover {
--color: green;
}
body:has(div:hover) {
--color: red;
}
@keyframes example {
0%, 100% { --color: hotpink; }
}
在这个例子里,即使期待 :hover 改变背景色,最终背景仍然是 hotpink,因为动画状态总是优先覆盖。
这也是为什么 CSS 不允许你直接在动画中改变动画控制属性,例如 animation-play-state。
2. 在 keyframe 中可以使用 var() 赋值属性
你可以在 @keyframes 内使用 CSS 自定义变量:
@keyframes example {
0%, 100% { background: var(--color); }
}
如果针对不同选择器改变 --color,动画会反映这些交互状态。
3. keyframe 结果中对 --var 的赋值会被缓存
例如:
body {
animation: example 1s infinite;
--color: blue;
background: var(--bg);
}
@keyframes example {
0%, 100% { --bg: var(--color); }
}
在这个例子中,无论后续变量 --color 如何改变,缓存的背景色保持第一次计算的值——不会重新计算。即使动画被暂停,缓存值仍然不会更新。
4. 改变动画属性会打破缓存机制
如果在不同状态下改变动画时长,例如:
body:hover {
animation-duration: 2s;
}
浏览器会重新计算动画缓存,这样你仍然可以根据用户状态得到不同结果。
注意:某些浏览器(例如 Safari)目前无法触发这种缓存重新计算。
5. 使用两个动画来组合逻辑
一个动画产生基础值,另一个动画负责修改变量,然后捕获值保存。这种组合可以根据状态和不同动画阶段重新计算变量。
🚀 CPU Hack 的核心实现
通过上面的观察我们知道:
- 从动画中获取的缓存值不会强制重新计算;
- 如果控制好时序,让两个动画不同时运行,那么可以避免循环依赖问题;
- 关键在于“捕获(capture)”并再次将值抛回输入(hoist)。
基本架构示例
body {
animation: hoist 1ms infinite,
capture 1ms infinite;
animation-play-state: paused, paused;
--input-frame: var(--frame-hoist, 0);
--frame-count: calc(var(--input-frame) + 1);
}
@keyframes hoist {
0%, 100% { --frame-hoist: var(--frame-captured, 0); }
}
@keyframes capture {
0%, 100% { --frame-captured: var(--frame-count); }
}
这个设计通过两个短时动画来捕获和提升帧计数值,从而绕过循环依赖。
🌀 用 DOM 交互控制动画状态
为了让动画阶段按顺序触发,可以使用 .phase-* HTML 结构和 :hover 触发不同动画状态:
<ol class="cpu">
<li class="phase-0"></li>
<li class="phase-1"></li>
<li class="phase-2"></li>
<li class="phase-3"></li>
</ol>
结合 CSS 判断层叠状态,就可以在不同阶段从捕获状态跳转到下一个阶段。
🧩 每个阶段的逻辑总结
| 阶段 |
状态 |
作用 |
|
| phase-0 |
hoist paused, capture 运行 |
捕获原始输出值 |
|
| phase-1 |
两者 paused |
冻结捕获值 |
|
| phase-2 |
hoist 运行, capture paused |
将捕获的值提升回输入 |
|
| phase-3 |
两者 paused |
再次冻结提升值,并回到 phase-0 |
|
🎉 CPU Hack 的结果与可能性
这个技术展示了:
- 完全使用 CSS 来“计算”整数;
- 根据屏幕尺寸执行逻辑;
- 捕获鼠标坐标;
- 实现类似游戏的逻辑,比如 CSS 实现的生命游戏或打砖块;
实际示例
游标于红蓝方块移动触发计算
https://codepen.io/propjockey/pen/wvRqWEy/86fc64e923093e4ce9e865bd0c9b8da7
hover 触发的按钮事件
https://codepen.io/propjockey/pen/ExGvgWy/9fbc3990ce135b77c2af8267dbd14a0c
简易CSS游戏
CSS Conway's Game of Life Simulator - Infinite Generations, 42x42
https://codepen.io/propjockey/pen/NWEYdjY
3D CSS游戏
https://garethheyes.co.uk/
x86 CSS模拟器
https://lyra.horse/x86css/