如何通过上传制作流畅的 CSS 动画
这次,我将处理来自Upload 的标题序列。
《上传》(Upload)是一部由格雷格·丹尼尔斯创作的美国科幻喜剧电视剧。故事发生在2033年,那时人类可以“上传”自己,进入自己选择的虚拟来世。程序员内森·布朗英年早逝,他被上传到豪华的湖景设施,却发现自己被控制欲极强、仍在世的女友英格丽德控制着,而英格丽德正是出钱让他住在那里的。
这部剧挺搞笑的,而且很适合极客题材。值得一看!
片头序列
下面是我们将要制作的第一季第二集的片头片段。
片头序列模仿了上传进度条,随着进度慢慢显示电视剧的名字。文字是透明的,下方显示一张图片,这是开场场景的第一帧。人们通常称之为“抠图文字”,即文字被剪掉,可以看到其背后的背景。
就在名字完全显露出来之前,它出现了故障,然后飞过观众,露出了开场场景。
TLDR
这是包含最终结果的代码笔。
如果你喜欢的话,请在 Codepen 上点个 ❤️!😊
设计考虑
经过一番搜索,我发现Paytone One字体与标题文字非常匹配。你可以在Google Fonts上找到它。
动画的核心部分是镂空文字。有很多方法可以实现这种效果,如果完全用 CSS 来实现就更好了,但也有一些需要注意的地方:
- 挖空文本会向观看者移动,而背景保持不变。这排除了将背景图像应用于实际文本的技术,例如使用
-webkit-background-clip: text;
。 - 故障效果在背景图像和文本之间添加了彩色元素。为了以最简单的方式实现此效果,最好将背景图像作为独立于镂空文本的元素,并在它们之间放置任何我们想要的内容。我们可以使用一个
clipPath
包含元素副本的text
,并将其应用于故障元素。这样可以确保不会溢出。但是,clipPath
即使故障很短暂(大约 300 毫秒),我们可能需要将文本向查看器移动的动画应用于 ,以保持同步。如果我们完全不移动故障元素,而只为其外观添加动画效果,性能会更高。
考虑到这一点,我认为在矢量图形编辑器中嵌入“挖空文本”会更容易。我可以把text
元素转换成 SVG 格式path
,然后将其与一个黑色矩形组合起来,创建一个单独的path
。
以下是我们想要创建的内容的视觉概览:
制作 SVG
我们首先手动创建 SVG。我们需要一个横向的 SVG,因此可以使用 来创建它,并将viewBox
宽度指定为 1200 个单位,高度指定为 800 个单位。我们需要一个填充整个画布的黑色矩形,因此我们将它的宽度设置为 1200,高度设置为 800。
我们将添加一个text
元素,并将其放置在画布的中心,这样我们就能看到它了!我们通过属性指定字体。我们先font-family
选择一个大号字体来看看它看起来是什么样子,然后给它一个白色,这样我们就可以在黑色矩形的映衬下看到它了。font-size
fill
<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>
现在,我们可以在Inkscape (或您最喜欢的矢量图形编辑器)中打开它。如果您使用 Inkscape,我建议您安装Inkscape 的 SVGO 插件。这将使您能够在保存文件时优化标记。
我将指导您在 Inkscape 中创建 SVG。
我首先检查的是文档属性。在菜单上,点击“文件”,然后点击“文档属性...”。它会打开以下选项卡。
画布尺寸似乎有点小,是 300px x 150px。让我们将其更改为 1200px x 800px,以匹配我们的viewBox
。
现在,我们来调整文本的大小和对齐方式。首先,我们可以点击文本工具(按字母T激活)来尝试更大的字体大小。我们将字体大小翻倍到 200px。
现在,让我们打开“对齐和分布”选项卡,以便将文本居中。在菜单上,点击“对象”,然后选择“对齐和分布...”。这样就会打开此选项卡。
点击“UPLOAD”text
元素。我们希望文本元素与页面对齐,并位于两个轴的中心:
- 检查下拉框中是否选择了“页面”
- 在“对齐”部分,点击“沿垂直轴对齐”按钮。它是第一行的第三个按钮,如下图绿色圆圈所示。
- 现在,点击“水平对齐”按钮。这是第二行的第三个按钮,如下图绿色圆圈所示。
结果如下:
标题的字母间距有点小,所以让我们调整一下文本来适应它。我尝试了一些值,结果发现-10 是最佳值,如下所示。
由于文本现在稍微窄了一点,让我们再次将其居中。重复我之前描述的步骤。
描边文本
我们要复制text
元素,用作描边轮廓。我们可以看看具体操作:在画布边缘创建一个矩形,并用红色填充。现在,选择并复制文本元素,并将其粘贴到红色矩形的顶部。现在,选择副本,并设置其样式。进入菜单,点击“对象”,然后选择“填充和描边...”以打开“填充和描边”选项卡。
现在,选择清除填充。我们在“填充”子选项卡中选择红色的“X”。接下来,转到“描边颜色”选项卡,选择白色。最后,转到“描边样式”子选项卡,选择 4px 作为描边宽度。
删除文本
我们希望将元素转换为路径,以便将它们集成为单个path
元素。选择黑色矩形和白色文本元素。在主菜单上,点击“路径”,然后选择“对象转路径”。现在,新近角度为 a path
,text
元素已变为一个包含 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>
稍后,我们将同时操作path
和text
,所以为了方便起见,我们先将它们分组。我会将它们包装在一个g
元素中,并赋予它一个id
“title”。
我们将添加一个黑色矩形来覆盖所有内容,稍后再添加动画来显示文本。我们称之为“显示”矩形。它的尺寸与画布相同。
<rect id="reveal" x="0" y="0" width="1200" height="800"></rect>
我们为白色光束创建第二个小矩形,用于跟踪标题显示进度。我们将它做得比较细,宽度为 10,高度可以与画布高度相同。我们给它的坐标设置x
为 -10,高度y
设置为 0。它位于“显示”矩形的左侧。我们暂时将它填充为白色,稍后可以进行调整,使其看起来更美观。
<rect id="beam" x="-10" y="0" width="10" height="800" fill="white"></rect>
我们将“光束”矩形放置在“标题”组下方(之前)。
把所有这些放在一起,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>
在这个阶段,我更喜欢尝试把已有的动画化。我有点迫不及待地想开始!如果你愿意,可以继续创作故障元素。
故障元素
故障效果乍一看可能很复杂,但其实也可以很简单。在这种情况下,故障效果发生得非常快,所以可能相当简陋和粗糙。
对我们来说,最好的切入点是从实际的片头序列截取一张截图。下面你可以看到,故障只是一些随机排列成条带状的彩色矩形。我们可以复制一下。
打开上一步的 SVG 文件,将“显示”矩形移到一边。我们只需绘制类似于上面参考的彩色矩形即可。这就是我的做法。
选择你制作的彩色矩形,并将它们分组。现在,将此组放在“标题”组下方。
接下来,我们要把这个组变成一个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>
然后在下面,在“光束”矩形之前,我们添加 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>
当我们手动编辑时,Inkscape 就无法显示我们的 SVG 了!我用 Firefox 打开它,看看效果如何:
为了暂时完成 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>
这部分最难。我的方法可能有点不正统,所以如果觉得有些奇怪也不用担心!所有编辑器在将绘制的图形转换为 SVG 元素时都有各自的局限性,编辑器可能会输出一些粗糙的标记。我不知道你是否应该遵循我的习惯,理想情况下,你应该在图形编辑器中完成所有的绘制和排列工作!
稍后我们可能需要在动画时进行一些调整。我怀疑可能需要将text
元素转换为 ,path
因为这在某些浏览器中可能比较麻烦。让我们开始吧!
基本 HTML 和 CSS
在开始动画之前,我们需要编写 HTML,并添加一些基本样式。
HTML
我们将制作的 SVG 封装在一个“容器”中div
。div
我们将背景图片添加到这个容器中。
<div class="container">
<svg viewbox="0 0 1200 800">
<!--more stuff here-->
</svg>
</div>
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%;
}
在某处加载字体
您可以通过以下方式加载字体:
- 添加资源以
head
从 Google Fonts/locally 加载它。 @import
向 CSS 文件添加语句。- 宣布它为一条
@font-face
规则。
我更喜欢绕过 Google 字体,所以我会在本地加载。当我在本地进行实验时,我选择了选项 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); }
}
就是这样:
这是它在 Chrome(Linux 和 Android)上的样子😵💫:
我确实尝试了使用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); }
}
就是这样:
我仍然对它在 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>
请注意,我们有一个,aria-label
以便文本仍然可以访问!
而且看上去更加顺畅。
我们将使用这个版本!
第二部分:标题的慢慢揭晓
显露动画需要将带有“reveal”字样的矩形id
以缓慢的方式移动到 SVG 画布上。它会快速显露单词的开头,但在大约三分之一处(字母“P”上方)停止,然后缓慢地移动到字母上方。此行为重复两次。它会快速跳转到三分之二处(字母“O”上方),然后缓慢显露字母的末尾部分。最后,重复此过程直至最后一个字母“D”。
我们将使用translateX()
正值的变换。这里没有什么秘诀,我们只需要尝试找到适合动画的正确输入值。我发现百分比最容易使用。
对于慢速移动部分,大约 33%、66% 和 90% 是比较好的初始输入translateX()
。对于慢速移动的距离,我们假设它移动了 5%。
跳到这些点的速度非常快,因此就时间而言,我们希望将少量的时间专用于中间的跳跃,让我们从 2% 开始。
前两次跳跃的第一次切入会是这样的:
- 从时间线的 0% 到 2%,总共向右移动 33%。
- 从时间线的 3% 到 30%,总共向右移动 38%。
- 从时间线的 31% 到 33%,总共向右移动 50%。
- 从时间线的 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%); }
}
现在我们来看:
第三部分:追踪标题揭示的领先光束
由于光束的移动遵循“显示”矩形,我只需复制“上传”@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%); }
}
这次我回头调整了 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%);
}
}
为了展示带有“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; }
}
事实证明,它太微妙了。我又添加了两个“故障”符号,总数达到了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>
您可以在完成的动画中看到最终结果。
完成的动画
源代码
源代码可以在这个 GitHub 仓库中找到。我很快会创建更多标题序列,并将它们也添加到仓库中。
另外,您还可以在此 codepen 集合中查看所有内容。
总结
如果你已经走到这一步,我向你致敬!👏
感谢您的阅读!欢迎订阅我的RSS 源,并在社交媒体上与他人分享这篇文章。💌如果您想表达您的感激之情,可以请我喝杯咖啡。😊
文章来源:https://dev.to/robole/how-to-make-a-slick-css-animation-from-upload-tv-series-title-sequence-2h8f