cyfung1031 发表于 前天 14:20

CSS编程 (Expert CSS: The CPU Hack)

本帖最后由 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` 的值:

```css
body {
--input-frame: var(--frame-count, 0);
--frame-count: calc(var(--input-frame) + 1);
}
```

剧透:**实际上你可以仅用 CSS 实现这一点,而不需要任何 JS,我会在下文展示如何做到!**

---

## 🧠 5 个可观察现象(The 5 Observables)

在正式演示之前,我们先了解 CSS 动画的一些高级行为,这样最终演示不会显得太突兀。

---

### 1. 动画状态优先于所有选择器状态(几乎总是)

动画状态中定义的属性会覆盖所有选择器状态中的属性。例如:

```css
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 自定义变量:

```css
@keyframes example {
0%, 100% { background: var(--color); }
}
```

如果针对不同选择器改变 `--color`,动画会反映这些交互状态。

---

### 3. keyframe 结果中对 `--var` 的赋值会被缓存

例如:

```css
body {
animation: example 1s infinite;
--color: blue;
background: var(--bg);
}

@keyframes example {
0%, 100% { --bg: var(--color); }
}
```

在这个例子中,无论后续变量 `--color` 如何改变,缓存的背景色保持第一次计算的值——不会重新计算。即使动画被暂停,缓存值仍然不会更新。

---

### 4. 改变动画属性会打破缓存机制

如果在不同状态下改变动画时长,例如:

```css
body:hover {
animation-duration: 2s;
}
```

浏览器会重新计算动画缓存,这样你仍然可以根据用户状态得到不同结果。

> 注意:某些浏览器(例如 Safari)目前无法触发这种缓存重新计算。

---

### 5. 使用两个动画来组合逻辑

一个动画产生基础值,另一个动画负责修改变量,然后捕获值保存。这种组合可以根据状态和不同动画阶段重新计算变量。

---

## 🚀 CPU Hack 的核心实现

通过上面的观察我们知道:

* 从动画中获取的缓存值不会强制重新计算;
* 如果控制好时序,让两个动画不同时运行,那么可以避免循环依赖问题;
* 关键在于“捕获(capture)”并再次将值抛回输入(hoist)。

### 基本架构示例

```css
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` 触发不同动画状态:

```html
<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/
页: [1]
查看完整版本: CSS编程 (Expert CSS: The CPU Hack)