如何通过上传制作流畅的 CSS 动画

2025-05-26

如何通过上传制作流畅的 CSS 动画

这次,我将处理来自Upload 的标题序列。

《上传》(Upload)是一部由格雷格·丹尼尔斯创作的美国科幻喜剧电视剧。故事发生在2033年,那时人类可以“上传”自己,进入自己选择的虚拟来世。程序员内森·布朗英年早逝,他被上传到豪华的湖景设施,却发现自己被控制欲极强、仍在世的女友英格丽德控制着,而英格丽德正是出钱让他住在那里的。

这部剧挺搞笑的,而且很适合极客题材。值得一看!

片头序列

下面是我们将要制作的第一季第二集的片头片段。

片头序列模仿了上传进度条,随着进度慢慢显示电视剧的名字。文字是透明的,下方显示一张图片,这是开场场景的第一帧。人们通常称之为“抠图文字”,即文字被剪掉,可以看到其背后的背景。

就在名字完全显露出来之前,它出现了故障,然后飞过观众,露出了开场场景。

TLDR

这是包含最终结果的代码笔。

如果你喜欢的话,请在 Codepen 上点个 ❤️!😊

设计考虑

经过一番搜索,我发现Paytone One字体与标题文字非常匹配。你可以在Google Fonts上找到它。

动画的核心部分是镂空文字。有很多方法可以实现这种效果,如果完全用 CSS 来实现就更好了,但也有一些需要注意的地方:

  1. 挖空文本会向观看者移动,而背景保持不变。这排除了将背景图像应用于实际文本的技术,例如使用-webkit-background-clip: text;
  2. 故障效果在背景图像和文本之间添加了彩色元素。为了以最简单的方式实现此效果,最好将背景图像作为独立于镂空文本的元素,并在它们之间放置任何我们想要的内容。我们可以使用一个clipPath包含元素副本的text,并将其应用于故障元素。这样可以确保不会溢出。但是,clipPath即使故障很短暂(大约 300 毫秒),我们可能需要将文本向查看器移动的动画应用于 ,以保持同步。如果我们完全不移动故障元素,而只为其外观添加动画效果,性能会更高。

考虑到这一点,我认为在矢量图形编辑器中嵌入“挖空文本”会更容易。我可以把text元素转换成 SVG 格式path,然后将其与一个黑色矩形组合起来,创建一个单独的path

以下是我们想要创建的内容的视觉概览:

这是我们将从后到前(从左到右)创建的元素的横截面

这是我们将从后到前(从左到右)创建的元素的横截面。

制作 SVG

我们首先手动创建 SVG。我们需要一个横向的 SVG,因此可以使用 来创建它,并将viewBox宽度指定为 1200 个单位,高度指定为 800 个单位。我们需要一个填充整个画布的黑色矩形,因此我们将它的宽度设置为 1200,高度设置为 800。

我们将添加一个text元素,并将其放置在画布的中心,这样我们就能看到它了!我们通过属性指定字体。我们font-family选择一个大号字体来看看它看起来是什么样子,然后给它一个白色,这样我们就可以在黑色矩形的映衬下看到它了。font-sizefill

<svg viewBox="0 0 1200 800">
    <rect x="0" y="0" fill="black" width="1200" height="800"></rect>
    <text x="600" y="400" font-family="Paytone One" font-size="100px" fill="white">UPLOAD</text>
</svg>
Enter fullscreen mode Exit fullscreen mode

现在,我们可以在Inkscape (或您最喜欢的矢量图形编辑器)中打开它。如果您使用 Inkscape,我建议您安装Inkscape 的 SVGO 插件。这将使您能够在保存文件时优化标记。

我将指导您在 Inkscape 中创建 SVG。

svg 在 inkscape 中的初始外观

我首先检查的是文档属性。在菜单上,点击“文件”,然后点击“文档属性...”。它会打开以下选项卡。

在 Inkscape 中设置文档属性

画布尺寸似乎有点小,是 300px x 150px。让我们将其更改为 1200px x 800px,以匹配我们的viewBox

现在,我们来调整文本的大小和对齐方式。首先,我们可以点击文本工具(按字母T激活)来尝试更大的字体大小。我们将字体大小翻倍到 200px。

现在,让我们打开“对齐和分布”选项卡,以便将文本居中。在菜单上,点击“对象”,然后选择“对齐和分布...”。这样就会打开此选项卡。

在 Inkscape 中对齐和分布选项卡

点击“UPLOAD”text元素。我们希望文本元素与页面对齐,并位于两个轴的中心:

  1. 检查下拉框中是否选择了“页面”
  2. “对齐”部分,点击“沿垂直轴对齐”按钮。它是第一行的第三个按钮,如下图绿色圆圈所示。
  3. 现在,点击“水平对齐”按钮。这是第二行的第三个按钮,如下图绿色圆圈所示。

使用 Inkscape 中突出显示的中心对齐按钮对齐和分布选项卡

结果如下:

Inkscape 中的标题居中

标题的字母间距有点小,所以让我们调整一下文本来适应它。我尝试了一些值,结果发现-10 是最佳值,如下所示。

在 Inkscape 中调整字母间距

由于文本现在稍微窄了一点,让我们再次将其居中。重复我之前描述的步骤。

描边文本

我们要复制text元素,用作描边轮廓。我们可以看看具体操作:在画布边缘创建一个矩形,并用红色填充。现在,选择并复制文本元素,并将其粘贴到红色矩形的顶部。现在,选择副本,并设置其样式。进入菜单,点击“对象”,然后选择“填充和描边...”以打开“填充和描边”选项卡。

复制文本,并在 Inkscape 中更改填充和描边

现在,选择清除填充。我们在“填充”子选项卡中选择红色的“X”。接下来,转到“描边颜色”选项卡,选择白色。最后,转到“描边样式”子选项卡,选择 4px 作为描边宽度。

带有白色描边的文本

删除文本

我们希望将元素转换为路径,以便将它们集成为单个path元素。选择黑色矩形和白色文本元素。在主菜单上,点击“路径”,然后选择“对象转路径”。现在,新近角度为 a pathtext元素已变为一个包含 6 个元素的组,每个字母path一个。path

如果字母位于一个组内,我们的下一步操作将无法成功。选中该组,右键单击,从上下文菜单中选择“取消组合”。现在选择矩形路径和 6 个字母路径。它应该如下所示。

选择文本组中的单个路径

请注意每条线周围的单独虚线path

现在,我们可以按想要的方式组合所有路径了。在主菜单上,点击“路径”,然后选择“排除”。现在,文本已被从矩形中剔除。

为了证明它有效,您可以将其拖到红色矩形上,看看它是如何成为一个用负空间代替文本的单个对象。

排除路径

合并和对齐

最后一步是将“描边文本”text元素精确地放置在挖空文本的上方。您可以在下面看到效果。

标题文本对齐

除了红色矩形之外,我们还可以在下方放置一个可以透过负空间的图像!

现在,您可以删除红色矩形!

揭示文本所需的要素

我将手工完成这一部分。

这是目前为止 SVG 的样子。

<svg viewBox="0 0 1200 800">
 <path d="m0 0v800h1200v-800zm618.9 328.1c14.4 0 27.132 3.0678 38.199 9.2012 11.2 6 19.867 14.465 26 25.398 6.2667 10.8 9.4004 23.267 9.4004 37.4 0 13.867-3.1337 26.268-9.4004 37.201-6.1333 10.8-14.8 19.265-26 25.398s-23.933 9.2012-38.199 9.2012c-14.4 0-27.2-3.0678-38.4-9.2012-11.067-6.1333-19.733-14.598-26-25.398-6.1334-10.933-9.2012-23.334-9.2012-37.201 0-14 3.0678-26.467 9.2012-37.4 6.2666-10.933 14.933-19.398 26-25.398 11.2-6.1333 24-9.2012 38.4-9.2012zm-431.4 3h45.199v80.6c0 8 1.9342 14 5.8008 18 3.8667 4 9.5334 6 17 6 7.4667 0 13.133-2 17-6 3.8667-4.1333 5.7989-10.133 5.7989-18v-80.6h45.201v80.6c0 11.733-2.8682 22.2-8.6015 31.4-5.7334 9.0667-13.799 16.134-24.199 21.201-10.267 5.0667-21.999 7.5996-35.199 7.5996s-25-2.5329-35.4-7.5996c-10.267-5.0667-18.267-12.134-24-21.201-5.7333-9.2-8.5996-19.667-8.5996-31.4zm150.8 0h49.6c20.533 0 36.4 4 47.6 12 11.333 8 17 19.2 17 33.6 0 9.0667-2.7985 17.001-8.3985 23.801-5.4666 6.8-13.002 12.067-22.601 15.801-9.4667 3.6-20 5.3984-31.6 5.3984h-8.4004v47h-43.199zm119.8 0h43.201v107.2h47.799v30.398h-91zm267.2 0h60.801l39.799 137.6h-47l-5.1992-26.398h-35.6l-5.8008 26.398h-45zm103.8 0h59.799c14.4 0 27.134 2.8663 38.201 8.5996 11.2 5.7333 19.867 13.733 26 24 6.2667 10.267 9.3984 22.001 9.3984 35.201 0 13.867-3.0659 26.066-9.1992 36.6-6 10.533-14.601 18.733-25.801 24.6-11.067 5.7333-23.933 8.5996-38.6 8.5996h-59.799zm-73.201 26.4-13.6 60.199h26.6zm-374.4 2.4004v33.6c2.6666 0.26667 5.5349 0.40039 8.6015 0.40039 7.0667 0 12.532-1.4671 16.398-4.4004 3.8666-2.9333 5.8008-7.2008 5.8008-12.801s-1.9342-9.7996-5.8008-12.6c-3.7334-2.8-9.1985-4.1992-16.398-4.1992zm237.2 3.4004c-5.4667 0-10.399 1.5988-14.799 4.7988-4.4 3.0667-7.8012 7.4-10.201 13-2.4 5.4667-3.5996 11.734-3.5996 18.801 0 10.667 2.6667 19.266 8 25.799s12.267 9.8008 20.801 9.8008c8.4 0 15.266-3.2674 20.6-9.8008 5.4666-6.5333 8.1992-15.132 8.1992-25.799 0-7.0667-1.2655-13.334-3.7988-18.801-2.4-5.6-5.8012-9.9333-10.201-13-4.4-3.2-9.4-4.7988-15-4.7988zm254.6 3.7988v65.6h13.6c10 0 17.601-3.1318 22.801-9.3984 5.3333-6.4 8-14.667 8-24.801 0-9.6-2.6008-17.201-7.8008-22.801-5.0667-5.7333-12.733-8.5996-23-8.5996z"/>
 <text x="174.1362" y="467.79831" fill="none" font-family="'Paytone One'" font-size="200px" letter-spacing="-10px" stroke="#ffffff" stroke-width="4">UPLOAD</text>
</svg>
Enter fullscreen mode Exit fullscreen mode

稍后,我们将同时操作pathtext,所以为了方便起见,我们先将它们分组。我会将它们包装在一个g元素中,并赋予它一个id“title”。

我们将添加一个黑色矩形来覆盖所有内容,稍后再添加动画来显示文本。我们称之为“显示”矩形。它的尺寸与画布相同。

<rect id="reveal" x="0" y="0" width="1200" height="800"></rect>
Enter fullscreen mode Exit fullscreen mode

我们为白色光束创建第二个小矩形,用于跟踪标题显示进度。我们将它做得比较细,宽度为 10,高度可以与画布高度相同。我们给它的坐标设置x为 -10,高度y设置为 0。它位于“显示”矩形的左侧。我们暂时将它填充为白色,稍后可以进行调整,使其看起来更美观。

<rect id="beam" x="-10" y="0" width="10" height="800" fill="white"></rect>
Enter fullscreen mode Exit fullscreen mode

我们将“光束”矩形放置在“标题”组下方(之前)。

把所有这些放在一起,SVG 看起来是这样的:

<svg viewBox="0 0 1200 800">
 <rect id="beam" x="-10" y="0" width="10" height="800"></rect>
 <g id="title">
 <path d="m0 0v800h1200v-800zm618.9 328.1c14.4 0 27.132 3.0678 38.199 9.2012 11.2 6 19.867 14.465 26 25.398 6.2667 10.8 9.4004 23.267 9.4004 37.4 0 13.867-3.1337 26.268-9.4004 37.201-6.1333 10.8-14.8 19.265-26 25.398s-23.933 9.2012-38.199 9.2012c-14.4 0-27.2-3.0678-38.4-9.2012-11.067-6.1333-19.733-14.598-26-25.398-6.1334-10.933-9.2012-23.334-9.2012-37.201 0-14 3.0678-26.467 9.2012-37.4 6.2666-10.933 14.933-19.398 26-25.398 11.2-6.1333 24-9.2012 38.4-9.2012zm-431.4 3h45.199v80.6c0 8 1.9342 14 5.8008 18 3.8667 4 9.5334 6 17 6 7.4667 0 13.133-2 17-6 3.8667-4.1333 5.7989-10.133 5.7989-18v-80.6h45.201v80.6c0 11.733-2.8682 22.2-8.6015 31.4-5.7334 9.0667-13.799 16.134-24.199 21.201-10.267 5.0667-21.999 7.5996-35.199 7.5996s-25-2.5329-35.4-7.5996c-10.267-5.0667-18.267-12.134-24-21.201-5.7333-9.2-8.5996-19.667-8.5996-31.4zm150.8 0h49.6c20.533 0 36.4 4 47.6 12 11.333 8 17 19.2 17 33.6 0 9.0667-2.7985 17.001-8.3985 23.801-5.4666 6.8-13.002 12.067-22.601 15.801-9.4667 3.6-20 5.3984-31.6 5.3984h-8.4004v47h-43.199zm119.8 0h43.201v107.2h47.799v30.398h-91zm267.2 0h60.801l39.799 137.6h-47l-5.1992-26.398h-35.6l-5.8008 26.398h-45zm103.8 0h59.799c14.4 0 27.134 2.8663 38.201 8.5996 11.2 5.7333 19.867 13.733 26 24 6.2667 10.267 9.3984 22.001 9.3984 35.201 0 13.867-3.0659 26.066-9.1992 36.6-6 10.533-14.601 18.733-25.801 24.6-11.067 5.7333-23.933 8.5996-38.6 8.5996h-59.799zm-73.201 26.4-13.6 60.199h26.6zm-374.4 2.4004v33.6c2.6666 0.26667 5.5349 0.40039 8.6015 0.40039 7.0667 0 12.532-1.4671 16.398-4.4004 3.8666-2.9333 5.8008-7.2008 5.8008-12.801s-1.9342-9.7996-5.8008-12.6c-3.7334-2.8-9.1985-4.1992-16.398-4.1992zm237.2 3.4004c-5.4667 0-10.399 1.5988-14.799 4.7988-4.4 3.0667-7.8012 7.4-10.201 13-2.4 5.4667-3.5996 11.734-3.5996 18.801 0 10.667 2.6667 19.266 8 25.799s12.267 9.8008 20.801 9.8008c8.4 0 15.266-3.2674 20.6-9.8008 5.4666-6.5333 8.1992-15.132 8.1992-25.799 0-7.0667-1.2655-13.334-3.7988-18.801-2.4-5.6-5.8012-9.9333-10.201-13-4.4-3.2-9.4-4.7988-15-4.7988zm254.6 3.7988v65.6h13.6c10 0 17.601-3.1318 22.801-9.3984 5.3333-6.4 8-14.667 8-24.801 0-9.6-2.6008-17.201-7.8008-22.801-5.0667-5.7333-12.733-8.5996-23-8.5996z"/>
 <text x="174.1362" y="467.79831" fill="none" font-family="'Paytone One'" font-size="200px" letter-spacing="-10px" stroke="#ffffff" stroke-width="4">UPLOAD</text>
 </g>
 <rect id="reveal" x="0" y="0" width="1200" height="800" fill="white"></rect>
</svg>
Enter fullscreen mode Exit fullscreen mode

在这个阶段,我更喜欢尝试把已有的动画化。我有点迫不及待地想开始!如果你愿意,可以继续创作故障元素。

故障元素

故障效果乍一看可能很复杂,但其实也可以很简单。在这种情况下,故障效果发生得非常快,所以可能相当简陋和粗糙。

对我们来说,最好的切入点是从实际的片头序列截取一张截图。下面你可以看到,故障只是一些随机排列成条带状的彩色矩形。我们可以复制一下。

故障参考截图

打开上一步的 SVG 文件,将“显示”矩形移到一边。我们只需绘制类似于上面参考的彩色矩形即可。这就是我的做法。

Inkscape 中的故障

选择你制作的彩色矩形,并将它们分组。现在,将此组放在“标题”组下方。

接下来,我们要把这个组变成一个symbol,这样就可以在多个地方重复使用它。你可以在 Inkscape 中做到这一点,但我觉得有点麻烦。我觉得最简单的方法是打开 SVG 源代码,自己进行编辑。稍后,我们将尝试使用故障的多个实例,我们将快速移动这些实例,以产生像素移动的效果。我们现在将添加 3 个实例,彼此略微偏移,稍后再看看如何处理。

我们创建一个defs,并将我们的矩形包裹在里面symbol,然后将其放置在里面。

 <defs>
        <symbol id="glitch">
            <rect x="374.28" y="452.4" width="7.5217" height="17.3" fill="#25db0f" fill-opacity=".60364" stroke-width=".75217"/>
            <rect x="371.12" y="452.4" width="3.1591" height="17.3" fill="#7d0000" fill-opacity=".7407" stroke-width=".75217"/>
            <!-- and so on -->
        </symbol>
 </defs>
Enter fullscreen mode Exit fullscreen mode

然后在下面,在“光束”矩形之前,我们添加 3 个use实例:

<use class="glitches" href="#glitch" x="0" y="100"></use>
<use class="glitches" href="#glitch" x="-50" y="50"></use>
<use class="glitches" href="#glitch"></use>
Enter fullscreen mode Exit fullscreen mode

当我们手动编辑时,Inkscape 就无法显示我们的 SVG 了!我用 Firefox 打开它,看看效果如何:

Inkscape 中的 3 个故障实例

为了暂时完成 SVG,我们将“显示”矩形恢复到其初始位置。我们还opacity="0"为所有 3 个use实例添加了隐藏功能,直到我们为它们添加动画效果为止。您可以查看 SVG 来查看标记。

完整的 SVG
<svg viewBox="0 0 1200 800">
    <defs>
        <symbol id="glitch">
            <rect x="374.28" y="452.4" width="7.5217" height="17.3" fill="#25db0f" fill-opacity=".60364" stroke-width=".75217" />
            <rect x="371.12" y="452.4" width="3.1591" height="17.3" fill="#7d0000" fill-opacity=".7407" stroke-width=".75217" />
            <rect x="454.37" y="458.82" width="72.8" height="11.3" fill="#4e420e" fill-opacity=".91041" stroke-width=".533" />
            <rect x="481.31" y="465.08" width="20" height="5.1" fill="#c9ad0d" fill-opacity=".54034" stroke-width=".92195" />
            <rect x="454.37" y="464.42" width="39.8" height="5.7" fill="#c9ad0d" fill-opacity=".54034" stroke-width=".7746" />
            <rect x="491.77" y="458.82" width="35.4" height="5.4" fill="#940a26" fill-opacity=".71372" stroke-width=".46456" />
            <rect x="899.8" y="377.94" width="69.2" height="23" fill="#f2d243" fill-opacity=".54905" />
            <rect x="825.33" y="341.5" width="40" height="22" fill="#076528" fill-opacity=".71372" stroke-width=".64734" />
            <rect x="831.89" y="351.12" width="45.8" height="20" fill="#d9a60f" fill-opacity=".71372" stroke-width=".78326" />
            <rect x="755.25" y="341.52" width="60" height="20" fill="#076508" fill-opacity=".71372" stroke-width=".75593" />
            <rect x="695.25" y="341.52" width="60" height="20" fill="#5f0765" fill-opacity=".71372" stroke-width=".60394" />
            <rect x="519.76" y="361.52" width="166.3" height="3" fill="#795736" fill-opacity=".71372" stroke-width=".86944" />
            <rect x="579.76" y="341.52" width="60" height="20" fill="#076465" fill-opacity=".71372" stroke-width=".75593" />
            <rect x="519.76" y="341.52" width="60" height="20" fill="#006c1c" fill-opacity=".71372" stroke-width=".75593" />
            <rect x="639.76" y="341.52" width="46.3" height="20" fill="#2616a7" fill-opacity=".71372" stroke-width=".51437" />
        </symbol>
    </defs>
    <use class="glitches" href="#glitch" x="0" y="100" opacity="0"></use>
    <use class="glitches" href="#glitch" x="-50" y="50" opacity="0"></use>
    <use class="glitches" href="#glitch" opacity="0"></use>
    <rect id="beam" x="-10" width="10" height="800" fill="white" />
    <g id="title">
        <path d="m0 0v800h1200v-800zm618.9 328.1c14.4 0 27.132 3.0678 38.199 9.2012 11.2 6 19.867 14.465 26 25.398 6.2667 10.8 9.4004 23.267 9.4004 37.4 0 13.867-3.1337 26.268-9.4004 37.201-6.1333 10.8-14.8 19.265-26 25.398s-23.933 9.2012-38.199 9.2012c-14.4 0-27.2-3.0678-38.4-9.2012-11.067-6.1333-19.733-14.598-26-25.398-6.1334-10.933-9.2012-23.334-9.2012-37.201 0-14 3.0678-26.467 9.2012-37.4 6.2666-10.933 14.933-19.398 26-25.398 11.2-6.1333 24-9.2012 38.4-9.2012zm-431.4 3h45.199v80.6c0 8 1.9342 14 5.8008 18 3.8667 4 9.5334 6 17 6 7.4667 0 13.133-2 17-6 3.8667-4.1333 5.7989-10.133 5.7989-18v-80.6h45.201v80.6c0 11.733-2.8682 22.2-8.6015 31.4-5.7334 9.0667-13.799 16.134-24.199 21.201-10.267 5.0667-21.999 7.5996-35.199 7.5996s-25-2.5329-35.4-7.5996c-10.267-5.0667-18.267-12.134-24-21.201-5.7333-9.2-8.5996-19.667-8.5996-31.4zm150.8 0h49.6c20.533 0 36.4 4 47.6 12 11.333 8 17 19.2 17 33.6 0 9.0667-2.7985 17.001-8.3985 23.801-5.4666 6.8-13.002 12.067-22.601 15.801-9.4667 3.6-20 5.3984-31.6 5.3984h-8.4004v47h-43.199zm119.8 0h43.201v107.2h47.799v30.398h-91zm267.2 0h60.801l39.799 137.6h-47l-5.1992-26.398h-35.6l-5.8008 26.398h-45zm103.8 0h59.799c14.4 0 27.134 2.8663 38.201 8.5996 11.2 5.7333 19.867 13.733 26 24 6.2667 10.267 9.3984 22.001 9.3984 35.201 0 13.867-3.0659 26.066-9.1992 36.6-6 10.533-14.601 18.733-25.801 24.6-11.067 5.7333-23.933 8.5996-38.6 8.5996h-59.799zm-73.201 26.4-13.6 60.199h26.6zm-374.4 2.4004v33.6c2.6666 0.26667 5.5349 0.40039 8.6015 0.40039 7.0667 0 12.532-1.4671 16.398-4.4004 3.8666-2.9333 5.8008-7.2008 5.8008-12.801s-1.9342-9.7996-5.8008-12.6c-3.7334-2.8-9.1985-4.1992-16.398-4.1992zm237.2 3.4004c-5.4667 0-10.399 1.5988-14.799 4.7988-4.4 3.0667-7.8012 7.4-10.201 13-2.4 5.4667-3.5996 11.734-3.5996 18.801 0 10.667 2.6667 19.266 8 25.799s12.267 9.8008 20.801 9.8008c8.4 0 15.266-3.2674 20.6-9.8008 5.4666-6.5333 8.1992-15.132 8.1992-25.799 0-7.0667-1.2655-13.334-3.7988-18.801-2.4-5.6-5.8012-9.9333-10.201-13-4.4-3.2-9.4-4.7988-15-4.7988zm254.6 3.7988v65.6h13.6c10 0 17.601-3.1318 22.801-9.3984 5.3333-6.4 8-14.667 8-24.801 0-9.6-2.6008-17.201-7.8008-22.801-5.0667-5.7333-12.733-8.5996-23-8.5996z" />
        <text x="174.1362" y="467.79831" fill="none" font-family="'Paytone One'" font-size="200px" letter-spacing="-10px" stroke="#ffffff" stroke-width="4">
            UPLOAD
        </text>
    </g>
    <rect id="reveal" x="0" y="0" width="1200" height="800" />
</svg>
Enter fullscreen mode Exit fullscreen mode

这部分最难。我的方法可能有点不正统,所以如果觉得有些奇怪也不用担心!所有编辑器在将绘制的图形转换为 SVG 元素时都有各自的局限性,编辑器可能会输出一些粗糙的标记。我不知道你是否应该遵循我的习惯,理想情况下,你应该在图形编辑器中完成所有的绘制和排列工作!

稍后我们可能需要在动画时进行一些调整。我怀疑可能需要将text元素转换为 ,path因为这在某些浏览器中可能比较麻烦。让我们开始吧!

基本 HTML 和 CSS

在开始动画之前,我们需要编写 HTML,并添加一些基本样式。

HTML

我们将制作的 SVG 封装在一个“容器”中divdiv我们将背景图片添加到这个容器中。

<div class="container">
  <svg viewbox="0 0 1200 800">
    <!--more stuff here-->
  </svg>
</div>
Enter fullscreen mode Exit fullscreen mode

CSS

我们给容器添加一些尺寸,并用 使其居中margin: 0 auto。我们将背景图像添加到容器中,并使其完全覆盖容器。

我们希望 SVG 作为覆盖层跨越容器的整个宽度。

body {
  margin: 0;
}

.container {
  width: 100%;
  max-width: 800px;
  margin: 0 auto;

  background-image: url("https://github.com/robole/title-sequences/raw/main/upload/img/background.jpg");
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
}

svg {
  width: 100%;
}
Enter fullscreen mode Exit fullscreen mode

在某处加载字体

您可以通过以下方式加载字体:

  1. 添加资源以head从 Google Fonts/locally 加载它。
  2. @import向 CSS 文件添加语句。
  3. 宣布它为一条@font-face规则。

我更喜欢绕过 Google 字体,所以我会在本地加载。当我在本地进行实验时,我选择了选项 3。

字体加载策略本身就是一个很深奥的话题,我现在就不展开了!关键在于让浏览器快速加载字体,我们不希望浏览器以难看的方式替换字体。

动画时间

我将动画分为 4 个部分:

  1. 将标题拉向观众,最终标题移到观众之外,让图片显露出来
  2. 标题以类似于上传栏的缓慢方式显示
  3. 追踪标题显示的领先光束
  4. 故障效应

第 1 部分:将标题呈现给观众

通常,最好从动画的这一部分开始,因为它对动画的其余部分影响最大。对文本进行变形操作存在一些陷阱。

transform: scale()您可以使用或实现相同的效果transform: translateZ()。我发现使用 效果通常更好scale()。Chrome 在文本变换时尤其敏感。原因之一是,为了提供硬件 3D 加速,Chrome 将 3D 变换后的元素视为纹理而不是矢量。这会导致文本在三维空间中移动时显得模糊。

我已用下面两种方式完成了此操作,因此您可以并排查看结果。

我测试了它们,它们在 Firefox 中看起来一样。然而,我发现这个translateZ()版本在 Chrome 上的行为有点奇怪,尤其是在移动设备上!

translateZ()

你可以找到合适的值来赋予透视图合适的“观看距离”。大多数情况下,我们会缓慢地沿着 Z 轴向正方向(朝向观看者)移动标题。

我们希望它最后移动得非常快,所以我们选择一个很大的值,translateZ()以便它能飞快地超出观众的视线。

@keyframes grow {
  from { transform: perspective(300px) translateZ(0); }
  90% { transform: perspective(300px) translateZ(60px); }
  to { transform: perspective(300px) translateZ(1000px); }
}
Enter fullscreen mode Exit fullscreen mode

就是这样:

这是它在 Chrome(Linux 和 Android)上的样子😵‍💫:

Chrome 中的生长动画

我确实尝试了使用transform-style和的一些变化perspective,但无论如何它看起来都一样。

scale()

与前面的例子类似,我们在大部分时间里缓慢增加文本的大小,直到 90%。对于最后的 10%,我们希望提供一个较大的值,scale()使其超出屏幕!

我们还将不透明度设置为零,因为我们需要让它消失。否则,我们会卡在字母“O”中心的黑色区域!此外,我translateX()在最后的变换中添加了一项,让它向右移动,使其呈指数增长以匹配原始形状。

@keyframes grow {
  90% { transform: scale(1.2); }
  92% {opacity: 1;}
  to { opacity: 0; transform: translateX(100px) scale(60); }
}
Enter fullscreen mode Exit fullscreen mode

就是这样:

我仍然对它在 Chrome 中的外观不太满意,有一点点卡顿,看起来文本在振动。

我可以将text元素转换为一组path元素,看看它是否会有所改进!

这就是它的样子:

    <g id="stroked-text" fill="none" stroke="#fff" stroke-width="4" aria-label="UPLOAD">
            <path d="m280.5 471.9q-19.8 0-35.4-7.6-15.4-7.6-24-21.2-8.6-13.8-8.6-31.4v-80.6h45.2v80.6q0 12 5.8 18t17 6 17-6q5.8-6.2 5.8-18v-80.6h45.2v80.6q0 17.6-8.6 31.4-8.6 13.6-24.2 21.2-15.4 7.6-35.2 7.6z" />
            <path d="m363.3 331.1h49.6q30.8 0 47.6 12 17 12 17 33.6 0 13.6-8.4 23.8-8.2 10.2-22.6 15.8-14.2 5.4-31.6 5.4h-8.4v47h-43.2zm51.8 62.8q10.6 0 16.4-4.4t5.8-12.8-5.8-12.6q-5.6-4.2-16.4-4.2h-8.6v33.6q4 0.4 8.6 0.4z" />
            <path d="m483.1 331.1h43.2v107.2h47.8v30.4h-91z" />
            <path d="m643.9 471.9q-21.6 0-38.4-9.2-16.6-9.2-26-25.4-9.2-16.4-9.2-37.2 0-21 9.2-37.4 9.4-16.4 26-25.4 16.8-9.2 38.4-9.2t38.2 9.2q16.8 9 26 25.4 9.4 16.2 9.4 37.4 0 20.8-9.4 37.2-9.2 16.2-26 25.4t-38.2 9.2zm0-36.4q12.6 0 20.6-9.8 8.2-9.8 8.2-25.8 0-10.6-3.8-18.8-3.6-8.4-10.2-13-6.6-4.8-15-4.8-8.2 0-14.8 4.8-6.6 4.6-10.2 13-3.6 8.2-3.6 18.8 0 16 8 25.8t20.8 9.8z" />
            <path d="m750.3 331.1h60.8l39.8 137.6h-47l-5.2-26.4h-35.6l-5.8 26.4h-45zm43.6 86.6-13-60.2-13.6 60.2z" />
            <path d="m854.1 331.1h59.8q21.6 0 38.2 8.6 16.8 8.6 26 24 9.4 15.4 9.4 35.2 0 20.8-9.2 36.6-9 15.8-25.8 24.6-16.6 8.6-38.6 8.6h-59.8zm57.8 101.6q15 0 22.8-9.4 8-9.6 8-24.8 0-14.4-7.8-22.8-7.6-8.6-23-8.6h-13.6v65.6z" />
        </g>
Enter fullscreen mode Exit fullscreen mode

请注意,我们有一个,aria-label以便文本仍然可以访问!

而且看上去更加顺畅。

我们将使用这个版本!

第二部分:标题的慢慢揭晓

显露动画需要将带有“reveal”字样的矩形id以缓慢的方式移动到 SVG 画布上。它会快速显露单词的开头,但在大约三分之一处(字母“P”上方)停止,然后缓慢地移动到字母上方。此行为重复两次。它会快速跳转到三分之二处(字母“O”上方),然后缓慢显露字母的末尾部分。最后,重复此过程直至最后一个字母“D”。

我们将使用translateX()正值的变换。这里没有什么秘诀,我们只需要尝试找到适合动画的正确输入值。我发现百分比最容易使用。

对于慢速移动部分,大约 33%、66% 和 90% 是比较好的初始输入translateX()。对于慢速移动的距离,我们假设它移动了 5%。

跳到这些点的速度非常快,因此就时间而言,我们希望将少量的时间专用于中间的跳跃,让我们从 2% 开始。

前两次跳跃的第一次切入会是这样的:

  1. 从时间线的 0% 到 2%,总共向右移动 33%。
  2. 从时间线的 3% 到 30%,总共向右移动 38%。
  3. 从时间线的 31% 到 33%,总共向右移动 50%。
  4. 从时间线的 33% 到 60%,总共向右移动 55%。

接下来,你需要不断调整这些值,直到你满意为止。以下是一些神奇的数字:

#reveal {
  animation-duration: 4s;
  animation-fill-mode: forwards;
  animation-iteration-count: 1;
  animation-name: reveal;
}

@keyframes reveal {
  3% { transform: translateX(30%); }
  30% { transform: translateX(37%); }
  33% { transform: translateX(54%); }
  60% { transform: translateX(60%); }
  63% { transform: translateX(85%); }
  99% { transform: translateX(91%); }
  to { transform: translateX(100%); }
}
Enter fullscreen mode Exit fullscreen mode

现在我们来看:

第三部分:追踪标题揭示的领先光束

由于光束的移动遵循“显示”矩形,我只需复制“上传”@keyframes并将其重命名为“跟随显示”即可。唯一需要更改的是初始跳跃阶段——我希望光束不可见。因此,我添加了scale(0)变换,将其缩小到无,然后scale(1)在应该可见时将其恢复到正常大小。由于发生得非常快,我们可以在随后的两次跳跃中不隐藏光束。

#beam {
  animation-duration: 4s;
  animation-fill-mode: forwards;
  animation-iteration-count: 1;
  animation-name: follow-reveal;
}

@keyframes follow-reveal {
  0% { transform: scale(0) translateX(37%); }
  3% { transform: scale(1) translateX(30%); }
  30% { transform: scale(1) translateX(37%); }
  33% { transform: translateX(54%); }
  60% { transform: translateX(60%); }
  63% { transform: translateX(85%); }
  99% { transform: translateX(91%); }
  to { transform: translateX(102%); }
}
Enter fullscreen mode Exit fullscreen mode

这次我回头调整了 SVG 文件,让光束看起来更加透明、模糊。我打开了 Inkscape 可以显示的 SVG 的早期版本,并更改​​了fill。我没有使用纯白色,而是使用了从右到左的白色和灰色线性渐变。然后,我通过菜单(“滤镜” > “模糊” > “模糊... ”)添加了模糊滤镜。希望这不会对动画速度造成负面影响,因为给任何东西添加模糊效果都会让我感到紧张!

这是一段视频,展示了两者的并排对比,左侧是调整后的版本,右侧是原始版本。试着在几个关键时刻暂停一下,看看有什么区别。

在我看来,它看起来稍微好一些。

第四部分:故障

再看一眼故障的参考镜头,您会注意到此时背景图像实际上也变得不饱和了。

故障参考截图

这需要两步。由于故障发生在序列开始后的3秒,我们需要将其延迟。为此,我们添加一个CSS变量,因为我们需要在几个地方使用它。

我尝试了一些值,grayscale()并使用saturate() CSS 滤镜函数,找到了grayscale()最佳效果。我们将它设置在彩色框步骤开始前 200 毫秒。

:root {
  --glitch-delay: 3s;
}

.container {
  /* other styles from before */

  animation-delay: calc(var(--glitch-delay) - 0.2s);
  animation-duration: 0.2s;
  animation-name: darken-bg;
}

@keyframes darken-bg {
  0%,
  100% {
    filter: grayscale(90%);
  }
}
Enter fullscreen mode Exit fullscreen mode

为了展示带有“glitches”类的彩色盒子,我们将使它们的动画效果交错。animation-delay为此,我们将为每个盒子赋予不同的属性。为了达到动画效果,我们将稍微移动它们(主要是向下移动),并调整不透明度,使其看起来更微妙。

我发现,一开始不透明度为 75% 时效果最佳,然后将其横向和纵向移动,最后完全淡出。

.glitches {
  animation-duration: 0.1s;
  animation-name: glitch;
}

.glitches:nth-of-type(1) {
  animation-delay: var(--glitch-delay);
}

.glitches:nth-of-type(2) {
  animation-delay: calc(var(--glitch-delay) + 0.025s);
}

.glitches:nth-of-type(3) {
  animation-delay: calc(var(--glitch-delay) + 0.05s);
}

@keyframes glitch {
  0% { opacity: 0.75; }
  40% { transform: translate(0, -3px); }
  80% { transform: translate(-20px, 0); }
  100% { opacity: 0; }
}
Enter fullscreen mode Exit fullscreen mode

事实证明,它太微妙了。我又添加了两个“故障”符号,总数达到了5个。我尝试了不同的位置,发现整体效果更好。

<svg>
    <!--other stuff-->
    <use class="glitches" href="#glitch" x="0" y="100" opacity="0"></use>
    <use class="glitches" href="#glitch" x="-50" y="50" opacity="0"></use>
    <use class="glitches" href="#glitch" x="20" y="20" opacity="0"></use>
    <use class="glitches" href="#glitch" opacity="0"></use>
    <use class="glitches" href="#glitch" x="-50" y="0" opacity="0"></use>
</svg>
Enter fullscreen mode Exit fullscreen mode

您可以在完成的动画中看到最终结果。

完成的动画

源代码

源代码可以在这个 GitHub 仓库中找到。我很快会创建更多标题序列,并将它们也添加到仓库中。

另外,您还可以在此 codepen 集合中查看所有内容

总结

电影《洛奇》中洛奇说“哟,艾德里安!我做到了!”的场景梗图

如果你已经走到这一步,我向你致敬!👏


感谢您的阅读!欢迎订阅我的RSS 源,并在社交媒体上与他人分享这篇文章。💌如果您想表达您的感激之情,可以请我喝杯咖啡。😊

文章来源:https://dev.to/robole/how-to-make-a-slick-css-animation-from-upload-tv-series-title-sequence-2h8f
PREV
为你的博客制作阅读进度条📊
NEXT
一次 Rust 并与 Android、iOS 和 Flutter 共享