Skip to content

动画 animation(Day14 3/3) ​

本页对应 Day14_CSS3,共 6 段视频,约 1 小时 45 分。

学习目标 ​

  • 能列举并对比 transition-timing-function 的常用关键字、steps() 与贝塞尔曲线,并说明其对过渡速度变化的影响。
  • 能正确书写 transition 复合属性,区分一个时间值与两个时间值分别表示持续时间、延迟,并为指定元素设置过渡效果。
  • 能独立实现图片放大旋转、遮罩层与图标淡入、背景图位置切换的悬停过渡案例。
  • 能分析定位与 translateZ 的层级影响,完成扑克牌正反面 3D 翻转过渡,并用父元素 hover 规避边缘闪烁。
  • 能使用 @keyframes 定义包含 from/to 或百分比关键帧的动画,并说明关键帧与元素之间的多对多绑定关系。
  • 能设置 animation-name、duration、delay、timing-function、iteration-count、direction、play-state、fill-mode 及复合属性,并预测和验证动画效果。

知识点 ​

先写关键帧,再给动画时间 ​

为什么需要:动画不是“写一个 transition 就完事”,它要先把变化过程拆成关键时刻。关键帧就是告诉浏览器“第 0% 长什么样、第 50% 长什么样、第 100% 长什么样”,中间由浏览器自动过渡。
怎么想:先定元素默认样式,再写 @keyframes。起始帧可用 from 或 0%,结束帧可用 to 或 100%,中间可写百分比。若起始帧与默认样式一致,可省略起始帧。
怎么做:

css
.box {
  width: 200px;
  height: 100px;
  background: #900;
  animation-name: 小乐;
  animation-duration: 5s;
}
@keyframes 小乐 {
  from { border-radius: 10px; }
  to   { border-radius: 50px; }
}

易错点:只写 animation-name 不会动,转写稿里就是补上 animation-duration 后才生效;动画默认执行一次,结束后回到没有关键帧的默认样式,不是停在结束帧。

动画的八个子属性与运动曲线 ​

为什么需要:动画要控制时长、延迟、速度曲线、次数、方向、播放状态和填充模式,单靠关键帧不够。
怎么想:前四个和过渡很像:animation-name、animation-duration、animation-delay、animation-timing-function。运动曲线默认 ease,是先加速后减速;linear 是匀速;ease-in 一直加速;ease-out 一直减速;ease-in-out 也是先加速后减速但更舒缓;steps(5, end) 是分步运动。
怎么做:

css
.ball {
  animation-name: roll;
  animation-duration: 2s;
  animation-delay: 1s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-direction: alternate;
  animation-play-state: running;
  animation-fill-mode: both;
}

易错点:steps(5, end) 默认在每一步末尾变化,step-start 相当于在每一步开始就变;转写稿里分成一步时看起来就像“没有过渡”,因为一步就是瞬间变化。

次数、方向、播放状态 ​

为什么需要:有时动画要循环、来回走、暂停。
怎么想:animation-iteration-count 默认 1,可写数字或 infinite;animation-direction 默认 normal,reverse 从结束帧到起始帧,alternate 交替,alternate-reverse 反向交替;animation-play-state 默认 running,paused 暂停。
怎么做:转写稿中用 :active 按住球暂停:

css
.ball:active {
  animation-play-state: paused;
}

易错点:alternate 要和多次执行配合才有“来回”效果;只执行一次时看不出交替。

fill-mode:动画之外的状态 ​

为什么需要:动画开始前、结束后,元素到底保持关键帧样式还是恢复默认,需要明确控制。
怎么想:默认 none,开始前和结束后都按无关键帧样式;forwards 结束后保持结束帧;backwards 开始前处于起始帧;both 两者都要。
怎么做:

css
.box {
  animation: roll 3s ease-in 2s both;
}

易错点:想看 backwards 的开始前状态,必须设置 animation-delay;没有延迟,一打开就从起始帧开始动,看不到“开始前”。forwards 和 backwards 单独用时只能顾一头,要两头都顾用 both。

复合属性与多个关键帧 ​

为什么需要:八个子属性写起来长,复合属性更简洁。
怎么想:animation: 名字 持续时间 延迟 曲线 次数 方向 状态 填充。时间和过渡一样:只写一个时间表示持续时间,写两个时间则第一个是持续时间、第二个是延迟。多个关键帧必须用逗号分组,每组时间、曲线各自配套。
怎么做:

css
.box {
  animation: roll 3s ease-in infinite alternate,
             jump 3s ease-in infinite alternate;
}

易错点:转写稿里把第二个关键帧直接接在后面导致不生效,必须用逗号隔开;复合属性会覆盖前面拆开写的动画子属性。

3D 翻转案例的调试思路 ​

为什么需要:扑克牌正反面叠在一起时,单纯 rotateY(180deg) 可能转过来仍看到背面图。
怎么想:两张图都定位,后写的背面图层级更高;要先建立层次感,让正面图或背面图在 Z 轴错开,3D 变换才占主导。转写稿中给正面图加 translateZ(-1px) 后,再转回来才能看到正面。
怎么做:

css
.wrap {
  perspective: 1000px;
  transform-style: preserve-3d;
}
.item {
  transition: transform .8s;
}
.wrap:hover .item {
  transform: rotateY(180deg);
}
.front {
  transform: rotateY(180deg) translateZ(-1px);
}

易错点:鼠标放在旋转元素边缘会闪烁,因为旋转后平面投影变窄,hover 失效;应把 hover 写在不旋转的父元素上:.wrap:hover .item。3D 与定位、z-index、translateZ 会互相影响,不能只记一条规则,要调参数验证。

过渡运动曲线速记 ​

为什么需要:动画的 animation-timing-function 与过渡曲线同源,理解过渡曲线能直接迁移。
怎么想:linear 是直线匀速;ease 默认先加速后减速;ease-in 越来越快;ease-out 越来越慢;ease-in-out 更平缓;贝塞尔曲线可用工具拖点生成;steps 是分步。
怎么做:在动画里写 animation-timing-function: linear; 或复合写 animation: roll 2s linear infinite;。
易错点:转写稿中“leaner”应纠正为 linear;渐变背景 background-image 不是数值/颜色,不能平滑过渡,转写稿里滚到一半突然变蓝就是这个原因。

作业目标与本节技术边界 ​

本节笔记列出的作业是:过渡“幽灵按钮”、过渡“第二个过渡案例”、动画“自行车手”、动画“loading动画”、3D变换“立方体(选做)”。转写稿没有逐行给出这五个作业的完整代码,而是集中讲了完成它们所需的核心技术:transition 运动曲线与复合属性、图片遮罩过渡案例、@keyframes 关键帧、动画相关属性,以及 3D 变换中的 transform-style: preserve-3d、perspective、translateZ、rotateY 等。因此下面按“为什么需要→怎么想→怎么做→易错点”整理成可复用的作业教程;未在材料中出现的素材和具体尺寸不编造,按课程素材替换即可。

过渡:幽灵按钮与第二个过渡案例 ​

为什么需要:按钮、图片遮罩如果从默认态到 hover 态瞬间跳变,会显得生硬;过渡让属性变化在时间轴上完成。怎么想:先确定“哪些属性变”,再确定“变多久、怎么变、是否延迟”。转写稿强调:transition 复合属性中如果只写一个时间,它是持续时间;写两个时间时,第一个是持续时间,第二个是延迟时间。怎么做:幽灵按钮可把背景、边框、文字颜色作为过渡属性,在默认态写 transition,在 :hover 改目标值;第二个过渡案例可参考转写稿的图片遮罩:图片放大并旋转,遮罩层由 opacity: 0 到 1,图标层也由透明到显示,并通过 background-position 切换图标状态。示例结构如下:

css
.item { transition: all .6s; overflow: hidden; position: relative; }
.item:hover img { transform: scale(1.3) rotate(10deg); }
.mask { opacity: 0; transition: opacity .6s; }
.item:hover .mask { opacity: 1; }

易错点:display: none 不能平滑过渡,转写稿明确改用 opacity;transition 要写在默认状态,否则鼠标移出时可能没有回退动画;图片放大要配合父级 overflow: hidden,否则会溢出并露出边角。

过渡运动曲线:ease、linear 与 steps 的调试 ​

为什么需要:同样是位移,匀速和先加速后减速的观感不同;作业中的按钮、遮罩、加载效果都需要选择合适节奏。怎么想:转写稿用“篮球和绿球”对比:默认 ease 是先加速后减速,linear 是匀速,ease-in 是一直加速,ease-out 是一直减速,ease-in-out 也是先加速后减速但更舒缓。怎么做:用 transition-timing-function 设置;需要复杂曲线时用 cubic-bezier(),转写稿建议用贝塞尔曲线工具拖点取参数;需要分步运动时用 steps(n, start|end)。调试时把两个球并排,观察谁先超过、谁最后同时到达,就能判断曲线差异。易错点:steps() 第二个参数默认是 end,表示每一步末尾才变化;start 是每一步开始就变化。转写稿还演示了 steps(1, end) 几乎等于“不过渡”,因为整段变化只在最后发生。贝塞尔曲线参数拖到范围外会产生越界运动,作业中不要误以为写错。

动画:自行车手与 loading 动画的关键帧 ​

为什么需要:过渡通常由 hover 等状态触发,而自行车手、loading 这类动画需要自动、循环、分阶段变化。怎么想:转写稿说“动画里已经把过渡囊括了”,先写关键帧规定关键时间点的样式,浏览器负责帧间过渡。怎么做:用 @keyframes 定义 from/to 或 0%/100%,同一元素可写多个百分比;只写结束帧时,默认样式会当作起始帧。转写稿示例:

css
@keyframes roll {
  from { border-radius: 10px; }
  to { border-radius: 50px; }
}
.box {
  animation-name: roll;
  animation-duration: 5s;
}

动画属性包括 animation-name、duration、delay、timing-function、iteration-count、direction、play-state、fill-mode。iteration-count: infinite 可无限循环;direction: alternate 让奇偶次往返;fill-mode: forwards 让结束保持结束帧,backwards 在延迟期间保持起始帧,both 两者都要。易错点:不写 fill-mode 时动画结束会回到默认样式;alternate 需要次数大于 1 才看得出交替;复合属性写多个动画时要用逗号分组,转写稿调试发现把第二个关键帧名字直接接在后面会导致时间等参数归属混乱,甚至不生效。

3D 变换:立方体(选做)的层次与调试 ​

为什么需要:立方体不是平面位移,必须让六个面在 3D 空间中有前后层次。怎么想:转写稿做扑克牌翻转时强调:先给父级开启 3D 空间并设观察距离,再让子元素旋转;如果两个定位元素在同一平面,浏览器仍按定位层级决定谁盖住谁,translateZ 不生效时先检查是否真的建立了层次感。怎么做:父级写 transform-style: preserve-3d; perspective: 1000px;,子面绝对定位,用 rotateY/rotateX 和 translateZ 错开前后;立方体六个面分别旋转并平移半个边长。转写稿调试扑克牌时,把 hover 挂到未变换的父级上,避免旋转后投影变窄导致鼠标“离开”元素而闪烁。易错点:只写 translateZ 但元素仍受定位和 z-index 影响;父级 overflow: hidden 可能裁剪 3D 效果;3D 与定位互相影响,规则复杂,作业中应通过调参数验证,而不是死记条目。

作业检查清单 ​

幽灵按钮检查 transition 是否写在默认态、hover 是否只改目标值;第二个过渡案例检查遮罩用 opacity 而非 display;自行车手和 loading 检查关键帧百分比、循环次数、fill-mode;立方体检查父级 3D 空间、透视距离、六个面的 translateZ 与旋转顺序。先做平面过渡,再做关键帧动画,最后做 3D,调试时用并排对比和辅助边框观察层级,能复现转写稿中的推导过程。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>过渡运动曲线对比</title>
  <style>
    .wrapper {
      width: 1000px;
      height: 200px;
      border: 4px solid #900;
      margin: 50px auto;
    }

    .ball {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      transition: margin-left 5s, transform 5s;
    }

    .ball1 {
      background: radial-gradient(circle at 30% 30%, #fff, #06f);
      transition-timing-function: linear;
    }

    .ball2 {
      background: radial-gradient(circle at 30% 30%, #fff, #090);
      transition-timing-function: ease;
    }

    .wrapper:hover .ball {
      margin-left: 900px;
      transform: rotate(3600deg);
    }
  </style>
</head>
<body>
  <div class="wrapper">
    <div class="ball ball1"></div>
    <div class="ball ball2"></div>
  </div>
</body>
</html>

要点:transition 写在默认状态 .ball 上,:hover 只写变化后的值;linear 是匀速,ease 是默认的平滑运动,先加速后减速;margin-left 和 transform 都参与过渡,所以两个球会一边右移一边旋转。
易错点:不要把 transition 只写在 :hover 里;多个过渡属性用逗号分隔;两个球最终都会到达终点,但中间速度不同,linear 与 ease 的对比要看过程而不是只看终点。

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>关键帧动画:小球滚动</title>
  <style>
    .wrapper {
      width: 1000px;
      height: 200px;
      border: 4px solid #900;
      margin: 50px auto;
      overflow: hidden;
    }

    .ball {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      background: radial-gradient(circle at 30% 30%, #fff, #999);
      animation: rollBall 5s linear 1s infinite alternate both;
    }

    .ball:active {
      animation-play-state: paused;
    }

    @keyframes rollBall {
      0% {
        transform: translateX(0) rotate(0deg);
      }

      40% {
        background: radial-gradient(circle at 30% 30%, #fff, #f00);
      }

      100% {
        background: radial-gradient(circle at 30% 30%, #fff, #06f);
        transform: translateX(900px) rotate(7200deg);
      }
    }
  </style>
</head>
<body>
  <div class="wrapper">
    <div class="ball"></div>
  </div>
</body>
</html>

要点:@keyframes 定义关键帧,animation-name 必须和关键帧名字一致;animation 复合属性中 5s 是持续时间,1s 是延迟时间;infinite 表示无限次,alternate 表示交替运动;:active 配合 animation-play-state: paused 可以按住暂停。
易错点:animation-duration 不写动画不会动;多个动画用逗号分隔;渐变色不是可插值属性,关键帧之间会跳变,若要平滑变色应使用 background-color;动画默认结束后恢复原样式,需要保持结束状态时用 animation-fill-mode: forwards 或 both。

常见错误 ​

  1. 现象:过渡/动画的“持续”和“延迟”写反,效果晚出现或时长不对 → 原因:复合属性中两个时间的顺序有约定 → 修法:按“持续时间 延迟时间”书写,只有一个时间时只当持续时间。
  2. 现象:元素从隐藏到显示没有过渡,瞬间切换 → 原因:用 display:none/block 做显隐 → 修法:改用 opacity,必要时配合 visibility 或 pointer-events。
  3. 现象:animation 复合属性里配置多个关键帧后,只有部分动画生效 → 原因:多个动画名及其时长、曲线等未按组用逗号分隔 → 修法:每个动画单独成组,组间用逗号隔开。
  4. 现象:动画播完立刻跳回初始样式 → 原因:未设置动画填充模式,默认不保留首尾状态 → 修法:按需使用 forwards 或 both,观察开始前状态时配合延迟。
  5. 现象:3D 翻转时正反面层级错乱,或鼠标在边缘反复闪烁 → 原因:定位层级与 Z 轴层次未协调;旋转后投影变窄导致 hover 离开 → 修法:用 translateZ 建立前后层次,并把 hover 触发放到未变换的父容器上。
  6. 现象:steps() 分步动画的变色/位移时机与预期不符 → 原因:第二参数 start/end 决定每步变化发生在步初还是步末 → 修法:分别测试 start 与 end,用颜色或短位移案例确认后再用于作业。

练习 ​

  1. 过渡:幽灵按钮——完成边框、背景、文字颜色的 hover 过渡,并控制好持续与延迟。
  2. 过渡:第二个过渡案例——按素材还原遮罩层、图标与图片的联动过渡。
  3. 动画:自行车手——用关键帧完成位移、旋转与循环,检查首尾衔接。
  4. 动画:loading 动画——用关键帧和运动曲线做出可循环的加载效果。
  5. 3D 变换:立方体(选做)——完成六个面的拼装、3D 空间开启与整体旋转。

原视频对照 ​

#视频时长
1过渡运动曲线25:58
2过渡复合属性02:27
3过渡案例14:35
43D变换过渡案例21:58
5关键帧14:43
6动画相关样式26:05

小结 ​

  1. 先调通静态样式,再加过渡或动画;不要用动画掩盖布局问题。
  2. 复合属性优先检查时间顺序与分组,尤其是多个动画并列时。
  3. 显隐动画优先选可过渡属性,避免用 display 硬切。
  4. 3D 效果要同时处理观察距离、Z 轴层次和 hover 触发区域。
  5. 关键帧决定过程,动画属性决定播放;首尾异常先查填充模式。
  6. 作业按“过渡按钮→遮罩案例→关键帧动画→3D 选做”递进完成。