3、CSS关键帧动画:@keyframes规则、animation属性、动画循环与方向控制

说实话,CSS动画是我在页面切换实战中最常用的技术之一。它不像JavaScript动画那样需要处理复杂的回调,也不依赖任何第三方库。你只需要写好@keyframes,再配上animation属性,一个流畅的页面过渡效果就出来了。

我记得刚入行那会儿,总觉得CSS动画是“玩具”,做不了复杂效果。直到有一次做移动端H5的页面切换,用JS动画在低端机上卡得不行,换成CSS关键帧动画后,丝般顺滑。从那以后,我对它的态度彻底变了。

3.1 @keyframes规则:动画的“剧本”

@keyframes说白了就是定义动画每一帧的样子。你告诉浏览器:从什么状态变到什么状态,中间怎么变,浏览器自己算。

基本写法有两种:

/* 方式一:from/to 关键词 */
@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 方式二:百分比节点(更灵活) */
@keyframes slideIn {
  0% {
    opacity: 0;
    transform: translateX(-100%);
  }
  50% {
    opacity: 0.5;
    transform: translateX(10%);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

我个人习惯用百分比写法。为什么?因为你可以插入中间状态,做更细腻的效果。比如一个弹入动画,先过冲再回弹,用from/to就做不到。

小技巧:我在项目中经常把关键帧拆成“进入”和“退出”两组。比如 @keyframes fadeIn 和 @keyframes fadeOut,命名清晰,维护起来也方便。

3.2 animation属性:把剧本搬上舞台

光有@keyframes还不够,你得用animation属性把它“挂”到元素上。这个属性是个简写,背后有8个子属性。我刚开始学的时候也觉得多,但用熟了发现,常用的其实就那几个。

子属性 作用 默认值
animation-name 指定@keyframes名称 none
animation-duration 动画时长 0s
animation-timing-function 速度曲线 ease
animation-delay 延迟启动 0s
animation-iteration-count 播放次数 1
animation-direction 播放方向 normal
animation-fill-mode 结束状态保持 none
animation-play-state 播放/暂停 running

简写示例:

.page-enter {
  animation: slideIn 0.6s ease-out 0.2s 1 normal forwards;
}

这个写法顺序是:名称、时长、速度曲线、延迟、次数、方向、填充模式。记不住顺序也没关系,我写代码时经常只写前两个,其他的按需追加。

重点: animation-fill-mode: forwards 在页面切换中特别有用。它让动画结束后保持最后一帧的状态,不会“闪回”到初始位置。我见过不少新手因为忘了这个,页面切完后元素突然跳回原位。

3.3 动画循环:一次、两次还是无限?

animation-iteration-count控制播放次数。取值很简单:

  • 数字:比如 2 表示播放两次,3.5 表示三次半
  • infinite:无限循环

页面切换动画通常用 1 次就够了。但有些场景需要循环,比如加载中的转圈动画:

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.loading-spinner {
  animation: spin 1s linear infinite;
}

我曾经犯过一个错:给页面退出动画设了 infinite。结果页面切走后,那个退出动画一直在后台循环,CPU占用直接拉满。嗯,这个坑你们别踩。

3.4 方向控制:正着放、倒着放、来回放

animation-direction有四个值,我列个表对比一下:

值 行为 适用场景
normal 从0%到100%正常播放 页面进入动画
reverse 从100%到0%反向播放 页面退出动画
alternate 先正后反,来回交替 悬停呼吸效果
alternate-reverse 先反后正,来回交替 与alternate起始方向相反

这里有个实用技巧:页面进入和退出可以用同一个@keyframes,只改方向。比如:

/* 同一个关键帧 */
@keyframes slideUp {
  from { transform: translateY(100%); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

/* 进入:正常播放 */
.page-in {
  animation: slideUp 0.4s ease-out forwards;
}

/* 退出:反向播放,效果就是滑出屏幕 */
.page-out {
  animation: slideUp 0.4s ease-in reverse forwards;
}

这样做的好处是少写一套@keyframes,代码更干净。我在做页面切换框架时,经常用这个模式。

注意: reverse 和 alternate 配合 animation-timing-function 时要小心。比如 ease-in 反向播放后实际效果变成了 ease-out。如果你需要精确控制,建议单独写退出关键帧。

3.5 知识体系总览

下面这张图是我自己梳理的CSS关键帧动画知识结构,你可以把它当做一个快速索引:

CSS关键帧动画知识体系 CSS关键帧动画 @keyframes 规则 animation 属性 from / to 关键词 百分比节点 (0%-100%) 循环控制 方向控制 数字 (1, 2, 3...) infinite normal / reverse alternate 💡 实战建议 进入动画用 normal + forwards,退出动画用 reverse + forwards 循环动画注意性能,避免在后台页面持续运行

3.6 实战中的避坑指南

最后分享几个我踩过的坑:

  • 动画不生效? 先检查 animation-name 和 @keyframes 的名字是否一致。我至少有三四次因为拼写错误找了半天。
  • 动画闪一下? 八成是忘了 forwards。加上 animation-fill-mode: forwards 就好了。
  • 页面切换卡顿? 尽量用 transform 和 opacity 做动画,别用 left/top/width/height。前者由GPU加速,后者触发重排。
  • 多个动画冲突? 一个元素可以同时挂多个动画,用逗号分隔:animation: fadeIn 0.3s, slideIn 0.5s。但要注意时间配合。

嗯,CSS关键帧动画的核心内容就这些。你把它理解成“定义动作 + 控制播放”,后面做页面切换时就会很顺手。