这篇 CSS 学习笔记将围绕 "亲吻小球" 动画案例展开,从 HTML 结构搭建到 CSS 核心技术应用,系统拆解水平垂直居中、面向对象 CSS、动画原理等关键知识点,帮你掌握高级 CSS 动画的实现逻辑。
CSS 高级动画学习笔记 ------ 从 "亲吻小球" 案例看 CSS 核心技术
在前端开发中,CSS 不仅是样式定义工具,更是实现交互与动效的核心载体。本次以 "亲吻小球" 动画案例为切入点,深入学习盒子水平垂直居中、面向对象 CSS 思想、CSS 动画原理等高级知识点,最终掌握从 0 到 1 实现复杂 CSS 动效的能力。

一、案例背景与核心目标
"亲吻小球" 案例通过两个黄色圆形小球(模拟人物),实现左侧小球靠近、右侧小球主动亲吻并出现 "爱心" 的动画效果。案例虽小,但涵盖 CSS 核心知识点,具体目标如下: 掌握盒子水平垂直居中的多种实现方式。 理解并应用面向对象 CSS(OOCSS)思想,遵循 DRY(Do Not Repeat Yourself)原则。 学会使用 CSS 伪元素、定位、动画属性,实现复杂交互动效。 理清 HTML 结构与 CSS 样式的对应关系,建立 "结构 - 样式" 分离的开发思维。
二、HTML 结构搭建:语义化与模块化
HTML 是 CSS 样式的载体,合理的结构设计能减少 CSS 代码量,提升可维护性。本案例的 HTML 结构遵循 "模块化" 原则,具体如下:
- 整体结构设计
            
            
              html
              
              
            
          
          <!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSS Animation</title>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <!-- 容器:统一控制两个小球的整体位置 -->
    <div class="container">
        <!-- 左侧小球(女主角):独立模块,包含自身面部元素 -->
        <div class="ball" id="l-ball">
            <div class="face face-l">
                <div class="eye eye-l"></div>
                <div class="eye eye-r"></div>
                <div class="mouth"></div>
            </div>
        </div>
        <!-- 右侧小球(男主角):独立模块,包含自身面部元素与亲吻特效 -->
        <div class="ball" id="r-ball">
            <div class="face face-r">
                <div class="eye eye-l eye-r-p"></div>
                <div class="eye eye-r eye-r-p"></div>
                <div class="mouth mouth-r"></div>
                <div class="kiss-m">
                    <div class="kiss"></div>
                    <div class="kiss"></div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>- 结构设计原则 语义化命名:类名与 ID 均反映元素功能,如 "container" 表示容器、"ball" 表示小球、"face" 表示面部,降低后期维护成本。 模块化拆分:每个小球作为独立模块,内部包含 "面部(face)- 眼睛(eye)- 嘴巴(mouth)" 等子元素,符合 "单一职责" 思想。 层级分明:通过嵌套关系体现元素层级(如 face 嵌套在 ball 内),与 CSS 定位层级相匹配,避免样式冲突。
三、CSS 基础样式:重置与通用设置
在编写具体样式前,需先进行 "样式重置" 与通用设置,消除浏览器默认差异,保证页面在不同浏览器中表现一致。
- 样式重置(Reset CSS)
            
            
              css
              
              
            
          
          * {
    margin: 0;
    padding: 0;
}作用:清除所有元素的默认 margin(外边距)与 padding(内边距),避免浏览器默认样式对布局的干扰(如 body 默认有 margin)。 原理:使用通配符 "*" 选择所有元素,统一设置 margin 与 padding 为 0,是前端开发中最常用的重置方式。
- 全局样式设置
            
            
              css
              
              
            
          
          body {
    background-color: green;
}作用:设置页面背景色为绿色,为动画提供清晰的视觉背景,突出小球元素。 技巧:全局样式(如背景、字体)建议写在 body 或 html 选择器中,避免重复设置。
四、核心技术 1:盒子水平垂直居中
"亲吻小球" 案例中,两个小球需整体在页面中水平垂直居中,这是 CSS 布局中的高频需求。本案例采用 "绝对定位 + transform" 实现,具体如下:
- 实现代码
            
            
              css
              
              
            
          
          .container {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 238px;
}- 原理拆解 绝对定位(position: absolute):将.container 从文档流中脱离,使其位置可通过 top、left 等属性控制。 定位基准:绝对定位的元素以最近的 "已定位祖先元素" 为基准,若没有则以 body 为基准。本案例中.container 以 body 为基准,设置 top: 50%、left: 50%,使其左上角与页面中心点对齐。 偏移修正(transform: translate (-50%, -50%)):由于 top: 50%、left: 50% 是基于元素左上角定位,会导致元素整体偏右下方。通过 translate (-50%, -50%),将元素自身向左、向上偏移 50%(基于元素自身宽高),最终实现完全居中。
- 其他居中方式对比 除 "绝对定位 + transform" 外,还有多种水平垂直居中方式,适用于不同场景: 实现方式 核心代码 适用场景 Flex 布局 .parent { display: flex; justify-content: center; align-items: center; } 子元素为块级 / 行内元素,需灵活控制子元素间距 Grid 布局 .parent { display: grid; place-items: center; } 单元素居中,代码简洁,兼容性稍差(IE 不支持) 绝对定位 + margin .child { position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto; } 子元素已知宽高,需兼容旧浏览器
五、核心技术 2:面向对象 CSS(OOCSS)
面向对象 CSS 是一种 CSS 编写思想,核心是 "抽离通用样式为基类,特殊样式为子类",遵循 DRY 原则,减少代码重复。本案例中 "小球""面部""眼睛" 等元素均采用此思想实现。
- 基类与子类的设计 以 "眼睛(eye)" 元素为例,两个小球的眼睛有通用样式(圆形边框),但右侧小球的眼睛边框方向不同(上边框 vs 下边框),具体实现如下: (1)基类:.eye(通用样式)
            
            
              css
              
              
            
          
          .eye {
    width: 15px;
    height: 14px;
    border-radius: 50%; /* 圆形效果 */
    border-bottom: 5px solid; /* 下边框(默认样式) */
    position: absolute;
}作用:定义所有眼睛的通用样式(宽高、圆形、定位),避免在每个眼睛元素中重复编写。 (2)子类:.eye-l、.eye-r、.eye-r-p(特殊样式)
            
            
              css
              
              
            
          
          .eye-l {
    left: 10px; /* 左侧眼睛的位置 */
}
.eye-r {
    right: 10px; /* 右侧眼睛的位置 */
}
.eye-r-p {
    border-top: 5px solid; /* 右侧小球眼睛的上边框 */
    border-bottom: 0px solid; /* 覆盖基类的下边框 */
}作用:子类仅定义与基类不同的特殊样式,通过 "基类 + 子类" 的组合(如<div class="eye eye-l eye-r-p"></div>),实现样式复用与差异化。 2. OOCSS 在案例中的完整应用 除眼睛外,"小球(ball)""面部(face)" 也采用 OOCSS 思想设计,具体如下: 小球(ball):.ball 为基类(定义宽高、圆形、边框、背景色),#l-ball、#r-ball 为子类(定义各自的动画)。 面部(face):.face 为基类(定义宽高、定位),.face-l、.face-r 为子类(定义左右面部的位置、动画)。
- OOCSS 的优势 减少代码量:通用样式只需编写一次,特殊样式仅补充差异部分,降低代码冗余。 提升可维护性:若需修改所有眼睛的大小,只需修改.eye 基类,无需逐个修改,避免遗漏。 增强扩展性:新增小球时,只需复用.ball 基类,再添加新的子类样式即可,无需重新编写所有样式。
六、核心技术 3:CSS 动画与伪元素
CSS 动画是实现 "亲吻小球" 交互效果的关键,通过@keyframes定义动画关键帧,结合animation属性绑定到元素上;伪元素则用于实现 "腮红" 等简单元素,减少 HTML 标签冗余。
- 伪元素的应用:实现腮红 案例中两个小球的腮红通过::before和::after伪元素实现,无需在 HTML 中新增标签,具体代码如下:
            
            
              css
              
              
            
          
          .face::before,
.face::after {
    content: ""; /* 伪元素必须有content属性,空值也需设置 */
    position: absolute;
    width: 18px;
    height: 8px;
    background-color: pink; /* 腮红颜色 */
    top: 20px;
    border-radius: 50%; /* 圆形腮红 */
}
.face::before {
    right: -8px; /* 左侧腮红位置 */
}
.face::after {
    left: -5px; /* 右侧腮红位置 */
}伪元素核心知识点 必须有 content 属性:即使不需要内容,也需设置content: "",否则伪元素不显示。 定位依赖:伪元素默认相对于父元素(.face)定位,需将父元素设置为position: relative,否则会相对于页面定位。 用途:常用于实现装饰性元素(如腮红、图标),或补充内容(如引号),减少 HTML 标签,保持结构简洁。
- CSS 动画:实现亲吻效果 案例中动画分为三部分:左侧小球靠近、右侧小球亲吻、亲吻时显示爱心,均通过@keyframes定义关键帧,结合animation属性绑定到元素上。 (1)左侧小球动画:.close
            
            
              css
              
              
            
          
          #l-ball {
    animation: close 4s ease infinite; /* 绑定动画:名称、时长、缓动、循环 */
    position: relative;
    z-index: 10; /* 层级高于右侧小球,避免被遮挡 */
}
@keyframes close {
    0% {
        transform: translate(0); /* 初始位置:不偏移 */
    }
    20% {
        transform: translate(20px); /* 20%时长时:向右偏移20px(靠近右侧小球) */
    }
    35% {
        transform: translate(20px); /* 20%-35%时长:保持位置(停顿) */
    }
    55% {
        transform: translate(0); /* 35%-55%时长:回到初始位置 */
    }
    100% {
        transform: translate(0); /* 55%-100%时长:保持初始位置 */
    }
}(2)右侧小球动画:.kiss
            
            
              css
              
              
            
          
          #r-ball {
    animation: kiss 4s ease infinite;
}
@keyframes kiss {
    40% {
        transform: translate(0); /* 初始位置:不偏移 */
    }
    50% {
        transform: translate(30px) rotate(20deg); /* 40%-50%时长:向右偏移30px+旋转20度(准备亲吻) */
    }
    60% {
        transform: translate(-33px); /* 50%-60%时长:向左偏移33px(亲吻左侧小球) */
    }
    67% {
        transform: translate(-33px); /* 60%-67%时长:保持位置(亲吻停顿) */
    }
    77% {
        transform: translate(0px); /* 67%-77%时长:回到初始位置 */
    }
}(3)爱心显示动画:.kiss-m
            
            
              css
              
              
            
          
          .kiss-m {
    position: absolute;
    left: 20px;
    top: 22px;
    opacity: 0; /* 初始透明:隐藏爱心 */
    animation: kiss-m 4s ease infinite;
}
@keyframes kiss-m {
    0% {
        opacity: 0; /* 0%-55%时长:隐藏 */
    }
    55% {
        opacity: 0;
    }
    66% {
        opacity: 1; /* 55%-66%时长:显示(亲吻时) */
    }
    66.1% {
        opacity: 0; /* 66.1%后:隐藏 */
    }
}CSS 动画核心知识点 animation 属性:由多个子属性组成,常用子属性如下: animation-name:动画名称(需与@keyframes名称一致)。 animation-duration:动画时长(如 4s 表示 4 秒)。 animation-timing-function:缓动函数(如 ease 表示 "慢 - 快 - 慢")。 animation-iteration-count:循环次数(infinite 表示无限循环)。 @keyframes 关键帧: 用百分比定义动画的 "关键节点",0% 为初始状态,100% 为结束状态。 可通过transform属性实现平移(translate)、旋转(rotate)、缩放(scale)等效果。 动画同步:多个元素的动画时长需保持一致(如均为 4s),通过调整关键帧的百分比,实现动画的先后顺序(如右侧小球在 40% 时开始动,左侧小球在 0% 时开始动)。
七、案例优化与扩展
基于 "亲吻小球" 案例,可从以下方面进行优化与扩展,进一步提升 CSS 技能:
- 兼容性优化 添加浏览器前缀:部分旧浏览器(如 Chrome 49-、Safari 9-)不支持animation和transform,需添加前缀:
            
            
              css
              
              
            
          
          @-webkit-keyframes close { /* Safari/Chrome前缀 */
    0% { -webkit-transform: translate(0); }
    /* 其他关键帧... */
}
#l-ball {
    -webkit-animation: close 4s ease infinite;
    animation: close 4s ease infinite;
}降级处理:为不支持动画的浏览器,设置静态样式(如两个小球默认靠近),保证基本视觉效果。 2. 性能优化 减少重排重绘:动画尽量使用transform和opacity属性,这两个属性不会触发重排(reflow),仅触发重绘(repaint),性能更优。 避免过度使用伪元素:伪元素过多会增加浏览器的渲染负担,复杂装饰建议使用图片或 SVG。 控制动画时长:动画时长不宜过长(建议 3-5s),避免用户等待时间过长。
八、总结与收获
通过 "亲吻小球" 案例的学习,系统掌握了 CSS 的核心技术,具体收获如下: 布局能力:熟练掌握盒子水平垂直居中的多种方式,能根据场景选择最优方案。 编码思想:理解面向对象 CSS(OOCSS)思想,遵循 DRY 原则,减少代码冗余,提升可维护性。 动效实现:掌握 CSS 动画的编写流程(定义关键帧→绑定动画),能通过transform和opacity实现复杂交互效果。 细节处理:学会使用伪元素减少 HTML 标签,通过z-index控制元素层级,避免样式冲突。
九、代码
            
            
              html
              
              
            
          
          <!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSS Animation</title>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <div class="container">
        <!-- 女主角 -->
        <div class="ball" id="l-ball">
            <div class="face face-l">
                <div class="eye eye-l"></div>
                <div class="eye eye-r"></div>
                <div class="mouth"></div>
            </div>
        </div>
        <!-- 男主角 -->
        <div class="ball" id="r-ball">
            <div class="face face-r">
                <div class="eye eye-l eye-r-p"></div>
                <div class="eye eye-r eye-r-p"></div>
                <div class="mouth mouth-r"></div>
                <div class="kiss-m">
                    <div class="kiss"></div>
                    <div class="kiss"></div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>
            
            
              css
              
              
            
          
          *{
    margin: 0;
    padding: 0;
}
body{
    background-color: green;
}
.container{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 238px;
}
.ball{
    border: 8px solid ;
    width: 100px;
    height: 100px;
    border-radius: 50%;/* radius意思是圆角,50%是圆形 */
    display: inline-block;/* 行内块元素,宽度高度可以设置 */
    position: relative;
    background-color: yellow;
}
.face{
    width:70px;
    height: 30px;
    /* border: 1px solid; 调试用border边框 */
    position: absolute;
    right: 0;
    top: 30px;
}
/* 伪元素选择器
css声明,像html一样来用,不需要在html声明
 一定要有content属性 忍者 */
/* 元素内容开始之前 */
.face::before,
.face::after{
    content: "";
    position: absolute;
    width: 18px;
    height: 8px;
    background-color: pink;
    top: 20px;
    border-radius:50%;
}
.face::before{
    right: -8px;
}
/* 元素内容结束之后 */
.face::after{
    left: -5px;
}
.eye{
    width: 15px;
    height: 14px;
    border-radius: 50%;/* 50%是圆形 */
    border-bottom: 5px solid;/* solid是实线 */
    position: absolute;
}
.eye-l{
    left: 10px;
}
.eye-r{
    right: 10px;
}
.eye-r-p{
    border-top: 5px solid;
    border-bottom: 0px solid;
}
.mouth{
    width: 30px;
    height: 14px;
    border-radius: 50%;
    border-bottom: 5px solid;
    position: absolute;
    bottom: -5px;
    left: 0;
    right: 0;
    transform: translate(3px);
    margin: auto; /* 平分左右的margin达到水平居中的效果 */
}
#l-ball{
    animation: close 4s ease infinite;
    position: relative;
    z-index: 10;/*  kiss ball 层级要大于 face ball 层级*/
}
@keyframes close{
    0%{
        transform: translate(0);
    }
    20%{
        transform: translate(20px);
    }
    35%{
        transform: translate(20px);
    }
    55%{
        transform: translate(0);
    }
    100%{
        transform: translate(0);
    }
}
.face-l{
    animation: face 4s ease infinite;
}
@keyframes face{
    0%{
        transform: translate(0) rotate(0deg);
    }
    10%{
        transform: translate(0) rotate(0deg);
    }
    20%{
        transform: translate(5px) rotate(-2deg);
    }
    28%{
        transform: translate(0) rotate(0);
    }
    35%{
        transform: translate(5px) rotate(-2deg);
    }
    50%{
        transform: translate(0) rotate(0deg);
    }
    100%{
        transform: translate(0) rotate(0deg);
    }
}
#r-ball{
    animation: kiss 4s ease infinite;
    
}
@keyframes kiss{
    40%{
        transform: translate(0);
    }
    50%{
        transform: translate(30px) rotate(20deg);
    }
    60%{
        transform: translate(-33px);
    }
    67%{
        transform: translate(-33px) ;
    }
    77%{
        transform: translate(0px);
    }
}
.mouth-r{
    animation: mouth-r 4s ease infinite;
}
.kiss-m{
    position: absolute;
    left: 20px;
    top: 22px;
    opacity: 0;
    animation: kiss-m 4s ease infinite;
}
.kiss{
    width: 13px;
    height: 10px;
    background-color: yellow;
    border-left: 5px solid;
    border-radius: 50%;
}
@keyframes kiss-m{
    0%{
        opacity: 0;
    }
    55%{
        opacity: 0;
    }
    66%{
        opacity: 1;
    }
    66.1%{
        opacity: 0;
    }
}
@keyframes mouth-r{
    0%{
        opacity: 1;
    }
    54.8%{
        opacity: 1;
    }
    66%{
        opacity: 0;
    }
    66.1%{
        opacity: 1;
    }
}
.face-r{
    left: 0;
    top: 37px;
}