会亲吻的小球!拆解 CSS 动画里的「球情侣」容器结构 1

54 阅读6分钟

我们在前几篇文章知道,在一个网页当中。HTML是未装修的房子,CSS是装修,js是行为交互。而亲吻小球这个网页该如何搭建HTML这个骨架呢?

image.png

我们把两个小球拆分开来看:

一个大容器.container它直接包裹着两个核心的小球容器 ——l-ballr-ball。这两个小球容器是同级关系,并排放在.container里。

还有其他容器吗?

我们可以看到它的五官:脸,嘴和眼睛这里可以统一放在face这个容器下。即每个小球容器内部又各包含一个脸部容器:l-ball容器里是.face.face-lr-ball容器里是.face.face-r

我们可以推断出脸部容器里再细分出五官相关的容器:.face-l里有三个子容器 ——.eye.eye-l.eye.eye-r.mouth.face-r里则有四个子容器 ——.eye.eye-l.eye-r-p.eye.eye-r.eye-r-p.mouth.mouth-r

还有----一个专门放 “吻” 的容器.kiss-m,而.kiss-m里面又包含两个.kiss容器。用于存放小球的亲吻动作。

好了,分析完毕,下面是HTML骨架的源码:

<!DOCTYPE html>
<html lang="en">
<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>

div层层嵌套可以用emmmt语法来简化:具体怎么做在系列里面的另一篇文章用HTML5、CSS和JavaScript创造代码敲击乐HTML5 敲击乐:用前端三剑客打造交互式音乐体验 在数字艺术与 - 掘金

接下来是CSS样式部分了,我们来重点探讨下下列知识点:

这是大容器.container的代码

.container {
    position: absolute;
    top: 50%;
    left: 50%;
    transform:translate(-50%,-50%);
    width: 238px;
    
}

第一:什么是文档流?

文档流是浏览器默认的布局方式,元素会按照它们在 HTML 中出现的顺序,从上到下、从左到右依次排列,像水流一样 “自然流动”。例如:块级元素(如 <div>)会独占一行,行内元素(如 <span>)会并排显示,每个元素都会占据一定的空间,后面的元素会 “尊重” 前面元素的位置,不会重叠。

第二:绝对定位position: absolute;如何脱离文档流

当 .container 被设置为 position: absolute 后: 它会 “跳出” 默认的文档流,不再遵循从上到下、从左到右的排列规则。原来在文档流中占据的空间会被 “释放”,后面的元素会像它不存在一样,直接填补它原来的位置

第三:这段代码是如何实现居中的?

这段 CSS 代码通过 绝对定位 + 位移变换 的组合,实现了 .container 元素在其父容器(通常是 <body> 或最近的定位父元素)中 水平和垂直方向都居中 的效果,具体原理如下:

1. position: absolute;将 .container 设置为绝对定位,使其脱离文档流,不再占据原来的空间,而是相对于 最近的已定位祖先元素(如果没有,则相对于 <html> 根元素)进行定位。

2. top: 50%; left: 50%; top: 50%:将元素的 顶部边缘 对齐到父容器高度的 50% 位置。 left: 50%:将元素的 左侧边缘 对齐到父容器宽度的 50% 位置。

此时元素的左上角顶点会刚好落在父容器的中心点,但元素整体并未完全居中(因为元素自身有宽度和高度,顶点居中≠整体居中)。

3. transform: translate(-50%, -50%); 这是关键的一步:通过 transform 的 translate 函数,将元素自身 向左移动自身宽度的 50%向上移动自身高度的 50% 。 由于 translate 的百分比是相对于 元素自身的宽高 计算的,因此这一步会把元素从 “左上角顶点居中” 调整为 “整体中心点居中”,最终实现完全居中。

两个小球的属性:

.ball {
    border: 8px solid;
    width: 100px;
    height: 100px;
    /* 圆角50就是圆*/
    border-radius: 50%;
    /* 既有行内元素,也不会 */
    display: inline-block;
    position: relative;
    background-color: white;
}

display: inline-block 控制元素的显示模式,是 inline(行内元素)和 block(块级元素)的混合体,作用是:

像行内元素一样排列:多个 inline-block 元素会在同一行显示(不会像块级元素那样独占一行),之间会保留空白间距(类似文字之间的空格)。例如:两个 .ball 小球设置为 inline-block 后,会并排显示(左右相邻),而不是上下堆叠。 像块级元素一样拥有宽高:可以通过 width 和 height 直接设置宽高(行内元素默认无法设置宽高)。例如:.ball 能通过 width: 100px; height: 100px 定义为正方形,再配合 border-radius: 50% 变成圆形。

简单说,inline-block 让小球既能并排显示(类似文字),又能精确控制大小(类似块级盒子)。

position: relative

控制元素的定位方式,作用是:

为子元素提供定位基准:当元素设置 position: relative 后,它的子元素如果使用 position: absolute(绝对定位),会相对于这个父元素进行定位(而不是相对于页面或其他祖先元素)。例如:.ball 内部的 .face(脸部)、.eye(眼睛)、.mouth(嘴巴)都使用了 position: absolute,它们的 top/left/right 等属性都是相对于 .ball 这个圆形元素计算的,从而能精准定位在小球内部(比如眼睛在球的上半部分,嘴巴在中间)。 不脱离文档流relative 定位的元素仍然占据原来在文档流中的空间,不会影响其他元素的布局(区别于 absolute 定位)。

行内元素和块级元素

特性display: inline(行内元素)display: block(块级元素)
布局并排显示,不独占一行独占一行,自动换行
宽高设置无效(由内容决定)有效(可自由设置)
宽度默认值由内容决定占满父容器宽度(100%)
margin 生效范围仅左右生效,上下无效上下左右均生效
padding 生效范围上下设置会显示但不挤压布局,左右正常上下左右均生效,会挤压布局

什么是伪代码?

.face::after, .face::before {
  content: "";
  position: absolute;
  width: 18px;
  height: 8px;
  /*小球腮红颜色*/
  background-color: #badc58;
  top: 20px;
  border-radius: 50%;
}
.face::before {
  right: -8px;
}

/* 元素内容结束之后 */
.face::after {
  left: -5px;
}

伪元素不是真实的 DOM 元素,不会出现在 HTML 代码里,只通过 CSS 生成并渲染在页面上。例如 .face::before 和 .face::after 会在 .face 元素的内容前后生成两个虚拟元素,但在浏览器的元素检查器中只能看到它们作为 CSS 生成的节点存在。

伪元素必须通过 content 属性定义内容(即使内容为空),否则不会显示。代码中 content: "" 表示伪元素为空内容,仅作为装饰性元素存在(这里用于实现小球的腮红效果)。

伪元素默认是行内元素,但通常会通过 position: absolute 使其脱离文档流,此时其定位基准是最近的已定位祖先元素(即设置了 position: relative/absolute/fixed 的父元素)。

伪元素可以设置 widthheightbackgroundborder 等所有 CSS 样式,与真实元素的样式控制方式一致。例如代码中通过 width: 18px; height: 8px; background-color: #badc58; border-radius: 50% 定义了腮红的大小、颜色和圆角(椭圆形)。

接下来的动画部分请看会亲吻的小球!拆解 CSS 动画里的「球情侣」容器结构 2