我用 CSS 拍了一部《星球大战》,连乔治·卢卡斯都惊了

72 阅读7分钟

🎬 用 CSS 拍一部《星球大战》?我这个“前端导演”真干了!

嘿,各位观众朋友!今天不聊框架、不卷算法,咱们来点浪漫的——用纯 CSS 拍一段《星球大战》片头!没错,就是那个经典的黄色大字从屏幕深处飞向宇宙尽头的镜头。没有摄像机,没有绿幕,只有一堆 <span> 和几行 @keyframes,但我这个“前端导演”愣是把它拍出来了!

lovegif_1765698628029.gif
温馨提示:最后会奉上源码和图片~~


🪐 场景一:搭建宇宙片场

首先得有个“摄影棚”。我的宇宙背景?一张 bg.jpg 足矣。

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

漆黑、深邃、带点星尘感——完美!接下来,我要把“STAR WARS”两个大字放进去。但等等,这可不是普通文字,这是电影的灵魂!所以我用了两个 SVG 图标(star.svg + wars.svg),高清、锐利、自带史诗感。

为了让它们出现在画面正中央,我祭出了前端导演的经典运镜手法:

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

这招叫“绝对居中大法”,百试不爽。哪怕宇宙膨胀了,我的字也稳如泰山!
使用position + transform 进行定位
position会相对离他最近的position不为static的外层元素进行定位
transform会相对自身的大小进行定位

当然了,对于刚入行的新手,如果摸不清自己的盒子到底在哪,可以使用我们的“幕布大法”-背景颜色调试大法

background-color: red;

屏幕截图 2025-12-14 140916.png


🚀 场景二:开拍!让文字“飞”起来

现在,灯光(CSS)打好了,演员(SVG)就位了,Action!

但怎么让“STAR WARS”像电影里那样从远处呼啸而来,又消失在深空?答案是:3D 镜头 + 关键帧剧本

我在 .starwars 容器上加了两行“导演指令”:

perspective: 800px;           /* 摄像机离舞台800px远 */
transform-style: preserve-3d; /* 别把3D压成2D!给我立体感! */

设置perspective代表我们声明了它支持3D 3D效果就表示我们拥有了Z轴上的动效,就是你面对着的屏幕的这个方向

首先在.star中声明

.star{
    /* 动画属性 
    star 动作脚本
    10s animation-duration
    ease-out animation-timing-function
    */
    animation: star 10s ease-out infinite;
}

参数说明:

  • star:脚本的名称
  • 10s:脚本动画的总时长
  • ease-out:ease-out 可不是什么神秘咒语——它其实是 动画节奏的“表演风格” ,专业点说,叫 缓动函数(easing function) 。ease-out代表先快后慢的呈现效果
  • infinite:循环播放脚本

然后给“star”写了一段表演脚本(也就是关键帧动画):

@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);//飞向远方~~消失不见
    }
    
}

参数解析:

  • opacity:模糊度
  • scale、translate:scale表示缩放大小,translate表示平移效果
  • n%:关键帧部分,我们写好关键帧部分,剩余的部分程序会自然过渡

“WARS”也差不多,只是稍微错开一点时间,让它俩像双人舞——不同步才真实,仿佛真有飞船在操控!

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

千万别小看这个细节,往往这种小细节才是抓住观众眼球的关键


💃 场景三:副标题的“钢管舞”彩蛋

重头戏来了!那句“The Force Awakens”是怎么做到每个字母都扭着腰进场的?

秘密就在这:我把每个字母都塞进一个 <span> 里!

<div class="byline">
  <span>T</span><span>h</span><span>e</span>...
</div>

为什么?因为 CSS 不会“认字”,它只认元素。想让每个字母单独旋转?那就得给它们每人一个“独立化妆间”!

然后,给每个 <span> 分配一段“钢管舞”动作:

@keyframes spin-letters {
  0%, 10% {
    opacity: 0;
    transform: rotateY(90deg); /* 侧身,你看不见我~ */
  }
  30% { opacity: 1; }        /* 转过来!看我! */
  70% { transform: rotateY(0); } /* 站直了,帅吧? */
  100% { opacity: 0; }       /* 谢幕,退场~ */
}
@keyframes move-byline {
    0%{
        transform: translateZ(5em);
    }
    100%{
        transform: translateZ(0);
    }
}

但是注意,想要能够实现这个钢管舞效果,你必须先通知好每一位演员

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

因为我们的行内元素并不支持3D效果,如果 span 还是 inline,浏览器会悄悄忽略大部分 transform 效果,或者表现得非常诡异——字母可能不动、错位,甚至直接“罢演”! 而设置为inline-block就会拥有块级元素的特性,每个字母就变成了一个可独立操控的小方块演员,既能乖乖排队,又能各自旋转、飞入、淡出——完美实现“逐字动画”!

是不是有点妖娆?但效果绝了!配上整行字缓缓从 Z 轴推进的动画(translateZ(5em) → 0),简直像在银河系走红毯!


🎥 导演手记:CSS 是我的摄影机

很多人觉得 CSS 只是“调颜色、改间距”,但在我眼里——
CSS 是镜头,是灯光,是运镜轨道,是特效合成台

  • perspective = 摄像机焦距
  • transform: translateZ() = 推轨镜头
  • @keyframes = 分镜脚本
  • opacity + scale = 情绪节奏

而那些 <span>?那是我的群演,每人一个特写,绝不糊弄!


🌟 最后一句台词

所以啊,别再说前端只是“切图仔”了。
我们可是用代码拍电影的人

下次你看到网页上的炫酷动画,别光说“哇好酷”,
想想背后那个熬夜调 ease-out 曲线的“导演”——
他可能正对着屏幕喃喃自语:

“这段 rotateY 再快0.1秒……对,就这样!原力与我同在!”


🌌 源码开源,欢迎 fork 并加入你的星战宇宙

<!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>
/*
  标准 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 {
    /* 声明 支持3D */
  perspective: 800px;
  /* 保持3D 变换 */
  transform-style: preserve-3d;
  /* 相对单位,相对于自身的字体大小 
    默认字体大小是16
  */
  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.3em;
  font-size: 1.6em;
  color: white;
}
.star{
    /* 动画属性 
    star 动作脚本
    10s animation-duration
    ease-out animation-timing-function
    */
    animation: star 10s ease-out infinite;
}
.wars{
    animation: wars 10s ease-out infinite;
}
.byline{
    animation: move-byline 10s linear infinite;
}
.byline span{
    display: inline-block;
    animation: spin-letters 10s linear infinite;
}

/* 设计动作 动画的关键帧 */
@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;

    }
    /* 模拟真实效果 不同步 更像是人在操控飞船 */
    90%{
        opacity: 1;
        transform: scale(1);
    }
    100%{
        opacity: 0;
        transform: translateZ(-1000em);
    }
}
@keyframes spin-letters {
   0%,10%{
        opacity: 0;
        /* 钢管舞 */
        transform: rotateY(90deg);
   }
  30%{
        opacity: 1;
      
  }
  70%,86%{
    transform: rotateY(0deg);
    opacity: 1;
  }
  95%,100%{
    opacity: 0;
  }
}
@keyframes move-byline {
    0%{
        transform: translateZ(5em);
    }
    100%{
        transform: translateZ(0);
    }
}

bg.jpg

star.svg

wars.svg

愿 CSS 与你同在!✨