CSS 动画并不只允许一个属性孤立变化。无论是卡片进入视口时同时发生的位移、缩放和淡入,还是按钮悬停时背景色与阴影的同步过渡,多属性动画的核心都在于让浏览器在关键帧之间对多个 CSS 属性同时进行插值计算。实际开发中经常被提到的 animation-property 并不是一个单独的 W3C 标准属性,而是对 animation 简写属性、animation-name、@keyframes 以及 transition-property 等能力的一种笼统叫法。把这一层概念理清之后,控制多属性动画会变得非常直接。

接下来的内容会先展示在同一组关键帧中声明多个属性的做法,再分析如何用逗号分隔多个动画来获得不同的节奏,最后对比 transition 与 animation 的适用边界,并梳理性能层面的注意点。
一、在单个 @keyframes 中同时声明多个属性
最常用的方法是在一个 @keyframes 规则里为不同的百分比帧同时写入多个需要变化的属性。浏览器在播放动画时,会针对每个属性单独进行插值,因此这些属性天然保持时间轴上的同步。比如要实现一个卡片从下方浮入、轻微放大并逐渐清晰的效果,可以把 opacity、transform 和 filter 写在同一组关键帧中。
@keyframes card-enter {
0% {
opacity: 0;
transform: translateY(24px) scale(0.96);
filter: blur(6px);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
filter: blur(0);
}
}
.card {
animation: card-enter 0.5s ease-out both;
}
这段代码中并没有为动画单独指定每一个属性,而是通过 animation 简写统一引用 card-enter 关键帧。关键帧内部三个属性在不同百分比之间都有明确的开始值和结束值,因此动画播放时 opacity 从 0 到 1、transform 从 translateY(24px) scale(0.96) 到 none、filter 从 blur(6px) 到 blur(0) 会并行进行。
这种写法的最大好处是同步性。它不需要你手动计算多个动画之间的延迟,也不会出现一个属性已经动画结束而另一个属性还没开始的情况。如果希望某一个属性晚一点启动,可以在关键帧中把它的初始值保持到更靠后的百分比,比如在 0% 和 20% 都保持 opacity:0,到 100% 才变为 1,这样就会产生 0.1 秒的延迟效果,以 0.5 秒总时长计算。对于大多数入场动效,这种单组关键帧方案足够直观且易于维护。
二、用逗号分隔多个动画实现不同节奏
当多个属性需要以完全不同的时长和缓动曲线变化时,把它们塞进同一组关键帧就显得不够灵活。CSS 的 animation 简写属性支持用逗号分隔多个独立动画,每个动画可以拥有自己的名称、持续时间、延迟、缓动函数甚至播放次数。代码看起来像这样:
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slide-up {
from { transform: translateY(40px); }
to { transform: translateY(0); }
}
.box {
animation:
fade-in 0.3s ease forwards,
slide-up 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
}
上面代码中 .box 会同时播放两个动画:淡入动画只持续 0.3 秒,位移动画持续 0.7 秒。由于两个动画分别作用于 opacity 和 transform,彼此不会覆盖,所以元素会在快速淡入的同时用更舒缓的曲线向上滑动。这种并行结构非常适合编排交错的 UI 动效。
使用逗号分隔时,需要注意 animation-timing-function、animation-delay 等子属性的值也按逗号与动画一一对应。第一个值属于第一个动画,第二个值属于第二个动画,如果数量不足则循环重复。例如 animation-delay: 0s, 0.1s; 表示第一个动画立即开始,第二个动画延迟 0.1 秒开始。这种对应关系一旦写错,动效的节奏就会与预期不符。
另一个容易踩的坑是两个动画同时修改同一个 CSS 属性。如果 fade-in 和 slide-up 都操作 transform,那么后面的动画声明会覆盖前面的效果,最终只有一个动画的 transform 生效。要避免这种情况,可以让不同动画负责不同属性,或者使用 animation-composition 属性来合并变换。在兼容性要求不高的项目里,后一种方案在 Chrome 中已经可以尝试。
三、transition-property 与 animation 的边界
很多开发者会把 transition-property 和 animation 弄混。transition-property 确实是一个标准属性,用于声明哪些属性在状态切换时需要过渡。它也可以同时列出多个属性,并通过逗号对应的 duration 和 timing-function 控制各自节奏。例如按钮悬停时,我们可以让背景色、盒阴影和缩放同时变化:
.btn {
background-color: #2563eb;
box-shadow: 0 2px 6px rgba(37, 99, 235, 0.3);
transform: scale(1);
transition-property: background-color, box-shadow, transform;
transition-duration: 0.3s, 0.4s, 0.2s;
transition-timing-function: ease, ease-out, cubic-bezier(0.34, 1.56, 0.64, 1);
}
.btn:hover {
background-color: #1d4ed8;
box-shadow: 0 8px 18px rgba(37, 99, 235, 0.45);
transform: scale(1.04);
}
transition 的优点是代码简单,它被动等待属性值发生变化,适合 hover、focus、类名切换等交互状态。但它无法自动播放,也不能像 animation 那样在页面加载后立即执行多轮循环或定义复杂的关键帧序列。因此如果你的需求是元素进入视口时自动播放一次入场动画,或者需要循环播放的加载指示器,还是应该使用 animation。
反过来,如果只是悬停或点击时的即时反馈,transition 往往比 animation 更合适。你可以用 transition-property: all 作为快速原型,但正式代码建议明确列出需要过渡的属性,避免未来无关属性变更也触发不必要的动画。多属性过渡时同样要遵守逗号一一对应的规则,duration 和 timing-function 的个数不足时浏览器会循环使用。
四、性能优化与降级策略
多属性动画会增加浏览器的合成与渲染开销,但并非所有属性成本相同。transform 和 opacity 是最适合高频动画的两个属性,因为它们通常只触发合成阶段,不会引起重排和重绘。filter 中的 blur 虽然视觉效果明显,但开销高于 transform,应谨慎使用。width、height、top、left、margin 等布局相关属性一旦写入关键帧,每一帧都可能触发布局计算,在低端设备上会明显掉帧。因此设计多属性动画时,优先用 transform 的 translate、scale、rotate 代替位置和尺寸变化。
如果确实需要驱动多个属性,可以在动画开始前通过 will-change: transform, opacity; 提示浏览器提前创建合成层。但不要给大量元素同时添加 will-change,否则会占用过多内存。另外还需要考虑前置减少动态效果的媒体查询 prefers-reduced-motion。对于开启了该选项的用户,应当关闭非必要的位移动画,只保留淡入淡出或完全静止。
最后给出一个完整的卡片列表入场示例,把同步多属性和逗号多动画结合起来。第一项使用单组关键帧,第二项使用两个逗号动画,你可以直接复制到页面中观察差异。实际项目中维护两套写法会增加成本,建议团队内部约定一种主风格,只在复杂动效处混合使用。
@keyframes item-enter {
0% {
opacity: 0;
transform: translateX(-16px) scale(0.98);
}
100% {
opacity: 1;
transform: translateX(0) scale(1);
}
}
@keyframes list-fade {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes list-slide {
from { transform: translateX(-24px); }
to { transform: translateX(0); }
}
.card-item {
animation: item-enter 0.45s ease-out both;
}
.card-item:nth-child(2) {
animation:
list-fade 0.25s ease forwards,
list-slide 0.6s cubic-bezier(0.22, 1, 0.36, 1) both;
}
这段代码展示了两种实现方式的并存。第一项动画由同一个关键帧同时控制透明度和位移,第二项则拆成两个动画分别控制透明度和位移。两者最终都能实现同时变化,但第二项可以更精细地调节每个属性的时序。你可以把 .card-item:nth-child(2) 的选择器改成实际业务中的类名,并根据需要调整时长与缓动函数。
理解多属性动画的逗号规则和关键帧插值机制之后,再复杂的动效编排也能被拆解成清晰的声明。剩下的工作就是选择合适的属性组合,在视觉表现与渲染性能之间找到平衡。
CSS动画animation-property多属性动画修改时间:2026-09-17 21:54:25