前端学习之CSS3 星球大战动画:从布局到 3D 动画的深度实践

80 阅读8分钟

前言:从“敲击乐”到“星际穿越”

前面我们学习了如何实现“敲击乐”钢琴的操作,小编觉得,不够,完全不够!前几天重温了经典科幻巨作《星球大战》,那标志性的金色字幕缓缓向上飞入浩瀚宇宙的画面,令人热血沸腾。于是,一个念头油然而生:能不能用纯 CSS 实现这样的“星际穿越”文字动画? 不依赖 JavaScript,仅靠 HTML 结构与 CSS3 的强大能力,打造一场视觉盛宴。今天,我们就来亲手制作这场“星际穿越”——用 CSS3 复刻《星球大战》开场动画!


页面链接:html5&css3 星球大战

页面效果图:

屏幕录制 2025-10-27 224535.gif


一、HTML 结构:语义化与组件拆解

首先,我们构建清晰的 HTML 结构:

html
预览
<div class="starwars">
  <img src="./star.svg" alt="star" class="star">
  <img src="./wars.svg" alt="wars" class="wars">
  <h2 class="byline" id="byline">
    <span>T</span><span>h</span>...<span>e</span>
  </h2>
</div>
  • <div class="starwars"> :作为整个动画的容器,语义上代表“星球大战”整体视觉单元。
  • 两个 <img> :分别承载 “STAR” 和 “WARS” 的 SVG 图标,比纯文字更易控制缩放与动画。
  • <h2 class="byline"> :副标题使用 <h2> 符合语义规范(主标题可为 <h1>),内部每个字母用 <span> 包裹,便于逐字动画控制

💡 面试题1:为什么要把所有字母用 <span> 包裹?

答:CSS 动画作用于单个元素。若想实现“逐字旋转/淡入”效果,必须将每个字符独立为可选中的 DOM 元素。<span> 是最轻量的行内容器,适合此类微操作。


二、CSS Reset:为何要“重置所有标签”?

style.css 开头,我们看到一段详尽的 CSS Reset:

css
编辑
html, body, div, span, h1, h2, ..., video {
  margin: 0;
  padding: 0;
  border: 0;
  font: inherit;
}

💡 面试题2:为什么不直接用 * { margin: 0; padding: 0; },而要逐个列出所有标签?

答:虽然 * 选择器看似简洁,但它存在两大问题:

  1. 性能开销大* 会匹配页面上每一个元素(包括伪元素),在大型项目中影响渲染性能;
  2. 无法重置继承属性:如 fontcolorvertical-align 等,这些属性浏览器默认值不同,必须显式重置。

显式列出常用标签的方式(源自 Eric Meyer Reset):

  • 更精准地覆盖浏览器默认样式;
  • 兼容性更好,尤其对表单、表格等复杂元素;
  • 避免意外影响第三方组件(如 Web Components)。

因此,大厂项目中常采用“精准重置 + 现代化补充”(如 box-sizing: border-box 全局应用)的策略。


三、布局核心:水平垂直居中与定位机制

css
编辑
.starwars {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

图片对比:

1.屏幕截图 2025-10-27 224910.png

2.点击top,left标签之后的图片效果/:屏幕截图 2025-10-27 224919.png (看不到的点击下)

是不是看到图片居中了呢,拿到源代码后可以去点击下元素自己试试。

💡 面试题3:position: absolute 如何找到“最近的非 static 定位祖先”?

答:CSS 定位规则如下:

  • 若元素设置 position: absolute/fixed,其定位参考点(containing block)由最近的祖先元素决定;
  • 该祖先必须满足:position 值为 relativeabsolutefixed 或 sticky(即非 static);
  • 若找不到,则以 <html>(初始包含块)为参考。

! 在本例中,body 默认 position: static,但 .starwarstop: 50% 是相对于 viewport(视口) 的,因为 body 高度为 100vh,且无其他定位祖先。transform: translate(-50%, -50%) 则基于元素自身宽高进行偏移,实现完美居中


四、3D 动画基础:perspective 与 transform-style

css
编辑
.starwars {
  perspective: 800px;
  transform-style: preserve-3d;
}
  • perspective: 800px:设置观察者距离 z=0 平面的距离为 800px,值越小,3D 透视感越强。
  • transform-style: preserve-3d:确保子元素在 3D 空间中保持深度,而非被“压扁”到父容器平面。

这两个属性是实现 translateZ 动画的前提。若缺少它们,translateZ(-1000em) 将无视觉效果。


五、动画细节解析:逐帧拆解关键帧

1. .star 与 .wars 动画

css
编辑
@keyframes star {
  0% { opacity: 0; transform: scale(1.5) translateY(-0.75em); }
  20% { opacity: 1; }
  89% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: translateZ(-1000em); }
}
  • 0% → 20% :淡入 + 从放大状态缩小;
  • 89% → 100% :快速沿 Z 轴飞远并消失;(注意为什么要将两个的即时帧率调节成不一样呢,因为两艘飞不因该是同时穿越,而应给是接而穿越,这种大片感,兄弟们自己想像下,对不对。)
  • 注意:translateY 在 89% 后被 translateZ 覆盖,因此垂直位置重置。

⚠️ 代码修正:scale(1.5)translateY(...) 必须加空格,否则无效!(写完时自动保存下就能调整好)

2. .byline span 逐字旋转动画

css
编辑
@keyframes spin-letters {
  0%, 10% { opacity: 0; transform: rotateY(90deg); }
  30% { opacity: 1; }
  70%, 86% { transform: rotateY(0); opacity: 1; }
  95%, 100% { opacity: 0; }
}

💡 面试题4:行内元素不能旋转,要转换成行内块元素?

答:正确!

  • <span> 默认是 display: inline不支持宽高、margin、transform 等盒模型属性

  • 但本例中我们设置了:

    css
    编辑
    .byline span {
      display: inline-block; /* 关键! */
      animation: spin-letters 10s linear infinite;
    }
    
  • inline-block 使 <span> 具备块级特性(可设置宽高、transform),同时保持行内流布局,实现逐字 3D 旋转


六、单位与响应式:em 与 vw 的巧妙运用

  • em:相对于当前元素 font-size,用于 translateY(0.5em)letter-spacing: 0.4em 等,实现字体相关的自适应间距
  • vw/vhbody { height: 100vh; } 确保全屏背景;
  • max-width: 100% :图片、视频等媒体元素自动缩放,防止溢出。

完整代码奉上:

/*
  标准 CSS Reset
  基于 Eric Meyer 的 Reset 并结合现代浏览器特性
*/

/* 所有元素应用 border-box 模型,方便布局 */
*,
*::before,
*::after {
    box-sizing: border-box;
}

/* 重置所有元素的内外边距、边框、字体等 */
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
}

/* HTML5 语义化元素设为块级 */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
    display: block;
}

/* 重置列表样式 */
ol,
ul {
    list-style: none;
}

/* 重置表格样式 */
table {
    border-collapse: collapse;
    border-spacing: 0;
}

/* 重置图片、视频等替换元素 */
img,
video,
canvas,
audio,
svg {
    display: block;
    max-width: 100%;
}

/* 重置表单元素 */
button,
input,
select,
textarea {
    /* 继承字体和颜色 */
    font: inherit;
    color: inherit;
    /* 移除默认边框和轮廓 */
    border: none;
    outline: none;
    /* 清除默认样式 */
    background: transparent;
    /* 统一垂直对齐 */
    vertical-align: middle;
}

/* 链接重置 */
a {
    text-decoration: none;
    color: inherit;
    /* 继承父元素颜色 */
}

/* 防止字体缩放 */
body {
    line-height: 1;
    -webkit-text-size-adjust: 100%;
}

/* 清除浮动(可选) */
.clearfix::after {
    content: "";
    display: table;
    clear: both;
}

body {
    height: 100vh;
    background: #000 url(./bg.jpg);
}

.starwars {
    /* 相对单位,相对于自身的字体大小 
    默认字体大小是16
  */
    /* 支持3d声明 */
    perspective: 800px;
    /* 保持3d变换 */
    transform-style: preserve-3d;
    width: 34em;
    height: 17em;
    /* 绝对定位 */
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    /* css 调试手法, 背景颜色调试大法 */
    /* background-color: red; */
}

img {
    width: 100%;
}

.star,
.wars,
.byline {
    position: absolute;
}

.star {
    top: -0.75em;
}

.wars {
    bottom: -0.5em;
}

.byline {
    left: -2em;
    right: -2em;
    top: 45%;
    /* background: green; */
    text-align: center;
    text-transform: uppercase;
    letter-spacing: 0.4em;
    font-size: 1.6em;
    color: white;
}

.byline span {
    display: inline-block;
    animation: spin-letters 10s linear infinite;
}

.star {
    /* 动画属性 */
    /* star  animation-name 动作脚本
    10s animation-duration
    ease out animation-time-function */
    /* infinite  animation-iteration-count 动画循环播放 */
    animation: star 10s ease-out infinite;
}

.wars {
    animation: wars 10s ease-out infinite;
}

/* 动作设计  动画的关键峥 */
@keyframes move-byline {
    0% {
        transform: translateZ(25em);
    }

    100% {
        transform: translateZ(0);
    }
}

@keyframes spin-letters {

    0%,
    10% {
        opacity: 0;
        /* 跳钢管舞 */
        transform: rotateY(90deg);
    }

    30% {
        opacity: 1;
        ;
    }

    70%,
    86% {
        transform: rotateY(0);
        opacity: 1;
    }

    95%,
    100% {
        opacity: 0;
    }
}

@keyframes star {

    /* 每个关键词写他的属性 */
    0% {
        opacity: 0;
        transform: scale(1.5)translateY(-0.75em);
    }

    20% {
        opacity: 1;
    }

    89% {
        opacity: 1;
        transform: scale(1);
    }

    100% {
        opacity: 0;
        transform: translateZ(-1000em);
    }
}

@keyframes wars {
    0% {
        opacity: 0;
        transform: scale(1.5)translateY(0.5em);
    }

    20% {
        opacity: 1;
    }

    91% {
        opacity: 1;
        transform: scale(1);
    }

    100% {
        opacity: 0;
        transform: translateZ(-1000em);
    }
}

@keyframes byline {

    /* 每个关键词写他的属性 */
    0% {
        opacity: 0;
        transform: scale(1.5)translateY(1em);
    }

    20% {
        opacity: 1;
    }

    90% {
        opacity: 1;
        transform: scale(1);
    }

    100% {
        opacity: 0;
        transform: translateZ(-1000em);
    }
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>html5&css3 星球大战</title>
    <link rel="stylesheet" href="./style.css">
</head>

<body>
    <div class="starwars">
        <img src="./star.svg" alt="star" class="star">
        <img src="./wars.svg" alt="wars" class="wars">
        <h2 class="byline" id="byline">
            <span>T</span>
            <span>h</span>
            <span>e</span>
            <span>F</span>
            <span>o</span>
            <span>r</span>
            <span>c</span>
            <span>e</span>
            <span>A</span>
            <span>w</span>
            <span>a</span>
            <span>k</span>
            <span>e</span>
        </h2>
    </div>
</body>

</html>

图片:

七、总结:CSS3 的导演艺术

bg.jpg

star.svg

wars.svg

大家动手做一做吧。

通过本次“星球大战”动画实践,我们深入掌握了:

  • CSS Reset 的工程化思维
  • 定位与居中的底层机制
  • 3D 变换的环境搭建(perspective + preserve-3d)
  • 关键帧动画的节奏控制
  • 行内元素与 transform 的兼容处理

前端不仅是写代码,更是用 CSS 当画笔、用 HTML 当舞台、用动画当镜头语言的导演艺术。正如《星球大战》所启示的:原力与代码,皆在你手中。

✨ 最终效果:金色字幕从屏幕中央浮现,缓缓放大、淡入,随后逐字旋转闪耀,最终飞向宇宙深处,消失于星河——这,就是 CSS3 的星际穿越!做自己的导演,干自己想做的事!每天一个小知识,大家都能进大厂!