这篇 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;
}