纯 CSS 无法真正递增数字,@property 是目前唯一能触发数值过渡的方案,但仅 Chrome 108+/Edge 109+ 支持;counter-increment + @keyframes 不可行,因其离散触发且 counter() 不支持插值;实际项目推荐 JS 驱动数值 + CSS 位移动画。

纯 CSS 无法真正“递增”数字,@property 是目前唯一能触发数值过渡的方案,但仅 Chrome 108+、Edge 109+ 支持,Safari / Firefox 完全不支持。
为什么 counter-increment + @keyframes 做不了递增动画
很多人尝试用 counter-reset 和 counter-increment 配合 @keyframes 实现滚动,结果数字只闪一下就跳到目标值,中间没有过渡。原因很直接:
-
counter-increment是离散触发的,浏览器不会为中间值(比如 3.5)生成内容 -
@keyframes里不能对counter()插值,它只在渲染时求值,不是响应式变量 -
steps(10)只是把动画切成 10 段,每段显示的仍是整数 counter 值,没有位移逻辑
@property 是唯一可行的纯 CSS 递增路径(但限制极多)
它让 CSS 自定义属性变成可动画的类型,配合 transition 或 @keyframes 才能产生平滑变化。但必须满足所有硬性前提:
- 必须声明
inherits: false和initial-value - 只能用于
类型(不能是,否则 Safari 直接忽略) - 动画目标值必须是合法数字,比如
100,不能是"100" - 需配合
content: attr(data-value)或 JS 同步更新 DOM 文本,否则视觉不更新
示例(仅现代 Chromium):
@property --num {
syntax: '<number>';
inherits: false;
initial-value: 0;
}
.number {
--num: 0;
animation: inc 2s forwards;
}
@keyframes inc {
to { --num: 123; }
}
.number::before {
content: attr(data-value);
}
/* 注意:这里仍需 JS 把 data-value 动态设为 Math.round(var(--num)) */
</number>
实际项目中推荐的方案:JS 驱动数值 + CSS 处理位移过渡
这是兼容性最好、控制最稳、视觉最丝滑的方式。核心是用 JS 控制目标值,CSS 负责每位数字的 translateY 滚动动画:
- 每位数字用等宽字体堆叠(
font-variant-numeric: tabular-nums),确保 0–9 宽度一致 - 显式声明字体链:
font-family: 'SF Mono', 'Consolas', 'Roboto Mono', monospace - 容器高度 = 单个数字行高,位移公式必须是
transform: translateY(calc(var(--digit) * -100%)) - 缓动必须用
cubic-bezier(0.33, 1, 0.68, 1),其他贝塞尔曲线会导致顿挫或错位 - 动画时长建议 0.3–0.6s,加
animation-fill-mode: forwards防止回弹
容易翻车的点:
-
calc(var(--digit) * 100%)→ 向下偏移,目标数字滚出视野 -
height: 48px但line-height: 46px→ 100% 基准失准,位移不准 - 用
em设容器高 → 父级font-size变动时,整个滚动节奏崩塌
真正的难点不在“怎么动”,而在“怎么动得准且稳”——字体宽度一致性、100% 基准的严格对齐、贝塞尔曲线的不可替代性,三者缺一不可。任何一处松动,滚动就会抖、错、卡。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











