结论:用::after伪元素+content动态换值+steps()控制帧切换是最轻量可控的纯css乱码动画方案;因css规范禁止对content做动画,必须预设关键帧并严格匹配steps(n,end),配合等宽字体与固定宽高防跳动。

直接说结论:用 ::after 伪元素 + content 动态换值 + steps() 控制帧切换,是最轻量、最可控的纯CSS乱码动画方案;但必须注意字符长度不一致会导致跳动,且无法响应文本变化。
为什么不能直接 animating content 值?
CSS 规范明确禁止对 content 属性做过渡或动画——浏览器会静默忽略 transition: content 或 animation 中的 content 变更。所以常见错误是写了一堆 0% { content: "a" } → 100% { content: "z" } 却完全没反应。真正生效的方式只有一种:把所有乱码字符串写死在 @keyframes 的各帧里,靠 steps() 强制逐帧跳转。
steps() 的步数和起始时机怎么配?
乱码动画本质是“离散状态切换”,不是平滑过渡,所以 steps() 的两个参数必须精准匹配:
- 第一参数(步数)要等于关键帧总数减 1,比如你写了 8 个
content值(0%、12.5%…100%),就得写steps(7),否则最后一帧会被跳过或卡住 - 第二参数推荐显式写
end(即steps(7, end)),避免默认行为在部分浏览器中表现不一致;start会让第一帧瞬间消失,容易造成闪屏 - 动画时长建议设为
0.8s ~ 1.2s,太短看不清字符,太长失去“乱码感”
如何避免乱码跳动、错位或宽度抖动?
乱码字符宽高不一(比如 "" 和 "@" 渲染宽度不同),直接替换会导致容器反复重排,视觉上“弹来弹去”。解决办法只有三个:
- 强制使用等宽字体:
font-family: monospace,哪怕乱码也按固定字宽对齐 - 给伪元素加
display: inline-block; width: 1ch;(1ch= 当前字体中 “0” 的宽度),锁死单字符占位 - 如果乱码含中文或全角符号,额外加
letter-spacing: -0.05em微调间隙,防止撑开容器 - 绝对不要用
white-space: pre-wrap或带空格的乱码串——空格会被压缩,导致节奏错乱
data 属性驱动乱码内容可行吗?
不行。content: attr(data-scramble) 只能读取一次初始值,无法随 data- 属性动态更新;想实现“悬停解密”或“点击切换乱码”,必须用 JS 修改 class 名,再靠不同 class 绑定不同 @keyframes,例如:
.scramble--error::after { animation: glitch-err 1s steps(5, end) infinite; }
.scramble--loading::after { animation: glitch-load 0.9s steps(6, end) infinite; }
这种模式下,乱码集是预置的,JS 只负责开关,既保性能又保可控性。
真正难的不是写几行 @keyframes,而是选哪 7~10 个乱码字符——既要视觉差异大(避免像 "0" 和 "O" 这种难分辨的),又要确保在目标浏览器里都能 fallback 渲染(比如避开某些 Unicode 私有区字符)。这点没人会提醒你,但上线后第一个用户截的图就是黑方块。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











