@property 是唯一使 css 自定义属性参与动画的机制,需显式声明 syntax(如'')、inherits: false、initial-value 且类型匹配,并在使用前注册;注册后通过 calc(var(--num) * -48px) 驱动 translatey 位移,配合等宽字体与 tabular-nums 实现数字滚动效果。

@property 是目前唯一能让 CSS 自定义属性参与动画的机制,但必须满足硬性条件才能生效,否则数字只会跳变、不动或报错。
为什么直接 transition --num 无效
CSS 原生不识别普通自定义属性是否可动画。写 transition: --num 0.4s 完全没反应——浏览器根本不会去解析这个变量的值变化。只有通过 @property 显式声明类型、继承性和初始值后,它才被纳入 CSS 动画系统。
-
syntax必须是带尖括号的合法类型,比如'<number>'</number>(单引号包裹,不能写成number或'number') -
inherits: false是强制要求,设为true在 Chromium 中会静默失效 -
initial-value类型必须与syntax严格匹配,<number></number>对应0,<length></length>对应0px - 注册必须放在
:root或目标元素作用域内,且在使用该变量的样式规则之前
@property 注册后怎么驱动滚动位移
注册只是第一步,真正实现“滚动感”靠的是把数字映射为 transform: translateY() 的负向位移,并用等宽字体对齐每一帧。
- 容器高度需等于单个数字高度(比如
font-size: 48px→ 容器height: 48px) - 数字堆叠用
span垂直排列,每行一个数字:0、1、2…9 - 位移公式固定为
transform: translateY(calc(var(--num) * -48px)),负号不可省,单位推荐px(比%更稳,避免 Safari 解析失败) - 必须加
font-variant-numeric: tabular-nums,并搭配等宽字体链,如font-family: 'SF Mono', 'Consolas', monospace
JS 更新值时要注意什么
动画由 JS 触发,但更新方式直接影响过渡是否平滑:
- 用
el.style.setProperty('--num', targetValue),不要用setAttribute或 innerHTML - 目标值必须是纯数字(
72),不能是字符串("72"),否则@property会拒绝解析 - 若需小数过渡(如 0→100.5),
syntax仍用'<number>'</number>,但注意最终显示逻辑要截断或四舍五入 - 避免高频连续 setProperty(如每帧都调),建议节流或用
requestAnimationFrame控制节奏
兼容性与降级要点
这不是渐进增强,而是“有就用,没有就 fallback”。Chromium 系(Chrome ≥108、Edge ≥109)支持完整特性;Safari 16.4+ 仅部分支持(<number></number> 可用,但 <integer></integer> 会被忽略);Firefox 当前完全不支持。
- 必须检测运行环境,可用
CSS.supports('animation-timeline', 'auto')或更直接地检查CSS.supports('property', '--test') - 降级方案只能是 JS 驱动 DOM 文本更新 + 简单 opacity/transform 进入动画,无法复现滚动效果
- 别在
@keyframes里尝试用counter()或attr()混合驱动——它们和@property不互通,也不会插值
真正卡住人的地方从来不是写法,而是 @property 的 syntax 格式、字体等宽验证、以及 JS 更新时传入了字符串而非数字——这三个点漏掉任意一个,整个动效就彻底静音。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











