CSS变量不能在@keyframes中直接使用,因关键帧规则静态固化、var()仅在样式应用时求值;正确做法是将变量定义在动画元素上,关键帧中用calc(var(--x, fallback))引用。

CSS 变量不能直接写进 @keyframes 里生效,这是规范限制,不是浏览器 bug。 想动态改关键帧数值(比如位移距离、缩放倍数、透明度终点),必须绕开“在 @keyframes 内用 var(--x)”这个死路,走元素样式驱动 + calc() 引用的路径。
为什么 @keyframes 里写 var(--x) 没反应
浏览器解析 @keyframes 时就把它固化成静态规则,var() 只在“样式应用到具体元素”那一刻才求值,而关键帧体(如 0% { opacity: var(--fade); })不属于该求值上下文。结果就是:不报错,但整行声明被静默丢弃。DevTools 的 computed 面板里根本看不到它。
常见错误现象包括:
-
transform: scale(var(--s))→ 动画卡在初始值,无变化 -
opacity: var(--fade)→ 元素始终不透明,像没加动画 - 用 JS 改了
document.documentElement.style.setProperty('--s', '1.5'),但动画毫无响应
正确做法:变量挂元素上,关键帧里用 calc(var(--x))
把变量定义在触发动画的元素(或其祖先)上,然后在 @keyframes 中用 calc() 包裹引用。这是唯一被所有主流浏览器(Chrome、Firefox、Safari)支持的方式。
示例:
@keyframes slide-x {
0% { transform: translateX(0); }
100% { transform: translateX(calc(var(--tx, 100px))); }
}
.element {
--tx: 200px;
animation: slide-x 0.4s ease-in-out;
}
实操要点:
- 务必提供 fallback:
var(--tx, 100px),否则未定义时calc()整体退为0 - 单位必须合法:
calc(var(--tx) * 1px)可以,calc(var(--tx) + 'px')是 JS 字符串拼接,在 CSS 里完全无效 - 变量必须定义在动画绑定的元素上(或
:root),document.body.style.setProperty()大概率失效,尤其在 Shadow DOM 下
JS 动态改值要小心作用域和时机
用 element.style.setProperty('--tx', '250px') 触发重绘的前提是:element 正是动画所绑定的那个元素(或其父级),且变量名大小写、前缀 -- 完全匹配。
高频修改(比如拖拽中实时更新 --tx)必须套 requestAnimationFrame(),否则样式刷新撕裂或卡顿。例如:
function updateTx(x) {
requestAnimationFrame(() => {
el.style.setProperty('--tx', `${x}px`);
});
}
容易被忽略的兼容性细节:
- Safari 15.4 之前不支持
cubic-bezier(var(--a), ...)这类函数参数内嵌变量,只能用于calc()中的属性值 -
animation-duration: var(--dur)可直接生效,但animation-name: var(--anim)无效——动画名不能动态化 - 如果动画已自然结束且没设
fill: 'forwards',改完变量也看不到效果,因为元素已回退到初始状态
真正难的不是语法,而是记住:变量不在关键帧里,而在元素上;不是靠重写 @keyframes,而是靠重算样式上下文。一旦漏掉 fallback、单位混用或作用域错位,整个链路就断了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











