从 `.face` 到动画 —— 用 HTML + CSS 构建可复用的表情动画

87 阅读3分钟

🎯 从 .face 到动画 —— 用 HTML + CSS 构建可复用的表情动画系统


📚 引言

在本次分享中,我们将通过一个“飞吻动画”案例,了解了 CSS 高级动画、结构化 HTML/CSS 编码、面向对象的 CSS(OOCSS)思想 以及 DRY 原则 的应用。

  • 先看效果图

屏幕录制 2025-10-30 231809.gif


🧱 第一步:HTML 结构设计

我们始终遵循“先写结构,再写样式”的原则。清晰的 HTML 是良好前端工程的基础。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>CSS 表情动画</title>
  <link rel="stylesheet" href="style.css" />
</head>
<body>
  <div class="container">
    <!-- 左边小球 -->
    <div class="ball" id="l-ball">
      <div class="face face-l"></div>
    </div>

    <!-- 右边小球 -->
    <div class="ball" id="r-ball">
      <div class="face face-r"></div>
      <div class="kiss-m">
        <div class="kiss"></div>
      </div>
    </div>
  </div>
</body>
</html>

✅ 设计思路说明:

  • 使用 .container 作为整体容器,负责水平垂直居中
  • 每个 .ball 是一个独立的表情球,内部包含 .face 面部组件。
  • .face-l.face-r.face多态扩展类,表示不同状态或位置的面部。
  • .kiss-m 是一个动画元素,用于实现“飞吻”效果。

🎨 第二步:CSS 布局与样式

🔑 关键技巧:绝对定位 + transform 居中和display: inline-block
.container { 
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 238px;
}
.ball{
    border: 8px solid;
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background-color: white;

    display: inline-block;
    position: relative;
}

为什么用 transform: translate(-50%, -50%)
它不依赖元素尺寸,适用于任何大小的容器,是最通用的居中方案之一
display: inline-block的作用?它让元素在同一行内水平排列,不会像块级元素一样独占一行


面部组件设计(面向对象的css)

我们采用 面向对象的 CSS ,将 .face 定义为“基类”,.face-l.face-r 为其“子类”(多态)。

.face {
  width: 70px;
  height: 30px;
  position: absolute;
  top: 30px;
}
🌱 多态实现:.face-l.face-r
.face-l {
  right: 0;
  animation: face 4s ease infinite;
}

.face-r {
  left: 0;
  top: 37px;
}

💡 核心思想
将样式拆分为可复用的“对象”,如 .face 是面部的通用样式,.face-l 是其具体实现,便于扩展和维护。


伪元素绘制“腮绿”

.face::before,
.face::after {
  content: "";
  position: absolute;
  background-color: #badc58;
  width: 18px;
  height: 8px;
  border-radius: 50%;
  top: 20px;
}

.face::before { right: -8px; }
.face::after  { left: -5px; }

⚠️ 伪元素要点

  • 必须写 content: "",否则不会渲染。

🎞️ 第三步:CSS 动画实现

1. 左球摆动动画

#l-ball {
  animation: close 4s ease infinite;
  z-index: 100;
  position: relative;
}

@keyframes close {
  0%   { transform: translate(0px); }
  20%  { transform: translate(20px); }
  35%  { transform: translate(20px); }
  55%  { transform: translate(0px); }
  100% { transform: translate(0px); }
}

2. 面部晃动(.face-l

.face-l {
  animation: face 4s ease infinite;
}

@keyframes face {
  0%,10%     { transform: translate(0) rotate(0); }
  20%,35%    { transform: translate(5px) rotate(-2deg); }
  28%,50%,100% { transform: translate(0) rotate(0); }
}

3. 右球飞吻动画

#r-ball {
  animation: kiss 4s ease infinite;
  z-index: 10;
}

@keyframes kiss {
  40%   { transform: translate(0); }
  50%   { transform: translate(30px) rotate(20deg); }
  60%   { transform: translate(-33px); }
  67%   { transform: translate(-33px); }
  77%   { transform: translate(0); }
  100%  { transform: translate(0); }
}

4. 嘴巴隐藏 + 飞吻出现(状态切换)

.mouth-r {
  animation: mouth-m 4s ease infinite;
}

.kiss-m {
  position: absolute;
  left: 20px;
  top: 22px;
  opacity: 0;
  animation: kiss-m 4s ease infinite;
}

@keyframes mouth-m {
  0% to 54.9% { opacity: 1; }
  55% to 66%  { opacity: 0; }
  66.1%       { opacity: 1; }
}

@keyframes kiss-m {
  0% to 55%   { opacity: 0; }
  66%         { opacity: 1; }
  66.1%       { opacity: 0; }
}

动画同步技巧:通过精确控制 opacitytransform 的关键帧,实现“嘴巴消失 → 飞吻出现 → 恢复”的流畅交互。
animation:动画名称 动画周期 动画曲线 重复次数
每个动画名称对应一个@keyframes规则,而@keyframes则用来定义动画的关键帧


📌 总结与延伸

通过这个小项目,我们实践了:

  1. HTML 结构先行:清晰的 DOM 是一切的基础。
  2. CSS 模块化设计:使用 OOCSS 和 DRY 原则组织样式。
  3. 动画精细控制:通过 @keyframes 实现复杂交互。
  4. 布局技巧transform: translate(-50%, -50%) 实现完美居中。