使用CSS与SVG重现卡通风格文本:动画设计实战

本文详细探讨了如何运用现代CSS(如text-shadow、text-stroke、background-clip等属性)和SVG技术,结合文本分割与动画关键帧,重现经典动画标题卡的卡通风格文本效果,并介绍了作者开发的配套工具。

重击动画第7部分:使用CSS与SVG重现卡通文本

作者:Andy Clarke

在本文中,先锋作者兼网页设计师Andy Clarke展示了使用现代CSS和SVG创建卡通文本标题的技术。

在完成一个需要我学习所有关于CSS和SVG动画知识的项目后,我开始撰写这个关于“重击动画”以及“经典卡通如何启发现代CSS”的系列文章。为了给今年画上句号,我想向您展示如何使用现代CSS来打造使卡通标题如此具有冲击力的元素:它们的排版。

标题艺术作品设计

在20世纪20年代和30年代初的默片时代,电影标题卡的排版营造了一种氛围,设定了场景,并提醒观众他们花钱看的是哪种类型的电影。

卡通标题卡同样是品牌、氛围和场景设定的结合体。在早期,当大型制片厂预算较大时,这些标题卡通常是插画式和绘画式的。

但是,当20世纪50年代电视蓬勃发展时,预算下降了,由像劳伦斯·“阿特”·戈布尔这样的艺术家设计的卡片采用了一种新的视觉语言,变得更加图形化、风格化,不那么复杂。

戈布尔不是角色动画师。他的职责是创造氛围,因此他为《摩登原始人》、《哈克贝利·猎犬》、《快手麦克劳》和《瑜伽熊》设计了环境,以及设定基调的开场标题卡。他那带有叠加标志的绘画标题卡帮助定义了汉纳-巴伯拉的标志性外观。

戈布尔为《快手麦克劳》和《瑜伽熊》等角色设计的艺术作品在较小的电视屏幕上效果显著。他没有复制卡通的静止画面,而是专注于呈现一个单一、强烈的想法——通常是剪影——来捕捉其精髓。在“嗡嗡熊”中,瑜伽熊乘坐直升机嗡嗡飞过。在“野餐上的熊”中,他手拿野餐篮蹦跳着离开,而在他的“拳击恐慌”中,瑜伽熊拳击着标题文字。

由于几乎或完全没有动态可依赖,戈布尔的单帧画面必须创造一种氛围、设定场景并描述一个故事。他们通过使用平面色彩、图形形状以及经常融入艺术作品的排版来实现这一点。

作为在网页上工作的设计师,卡通标题可以教会我们很多关于如何传达品牌个性、给人留下第一印象,并为用户使用产品或网站时的体验设定期望。我们可以从艺术家的技巧中学习,以创建有效的横幅、落地页页眉,甚至是经典的开机画面。

卡通标题排版

卡通标题卡展示了如何将字体与图像融合,以提供标题或英雄区域所需的冲击力。借助一些text-shadow、text-stroke和transform技巧,现代CSS让您可以利用同样的能量。

卡通文本标题生成器

在撰写本文的过程中,我意识到有一个工具来生成像我如此喜爱的卡通标题风格的文本会很有用。所以我做了一个。

我的卡通文本标题生成器让您可以尝试颜色、描边和多重文本阴影。您可以调整绘制顺序,应用字间距,在选定的示例字体中预览文本,然后将生成的CSS直接复制到剪贴板以在项目中使用。

卡通标题CSS

您可以简单地复制粘贴卡通文本标题生成器提供的CSS。但让我们更仔细地看看它做了什么。

文本阴影

看看《奥吉小狗》剧集“Yuk-Yuk Duck”中的字体,其淡黄色字母和深色、硬朗、偏移的阴影将其从背景中抬起并创造了深度错觉。

您可能已经知道text-shadow接受四个值:(1)水平偏移和(2)垂直偏移,(3)模糊度,以及(4)可以是纯色或半透明的颜色。这些偏移值可以是正数或负数,因此我可以使用一个向右下方拉动的硬阴影来复制“Yuk-Yuk Duck”:

1
2
color: #f7f76d;
text-shadow: 5px 5px 0 #1e1904;

另一方面,这个“Pint Giant”标题带有负的半软阴影,感觉不同:

1
2
3
4
color: #c2a872;
text-shadow:
  -7px 5px 0 #b100e,
  0 -5px 10px #546c6f;

为了增加额外的深度并创造更有趣的效果,我可以分层多个阴影。对于“Let’s Duck Out”,我组合了四个阴影:第一个是具有负水平偏移的实心阴影,用于将文本从背景中抬起,随后是逐渐柔和的阴影以在其周围创建模糊效果:

1
2
3
4
5
6
color: #6F4D80;
text-shadow:
  -5px 5px 0 #260e1e, /* 阴影1 */
  0 0 15px #e9ce96,   /* 阴影2 */
  0 0 30px #e9ce96,   /* 阴影3 */
  0 0 30px #e9ce96;   /* 阴影4 */

这些阴影表明,使用text-shadow不仅仅是为了创建照明效果,它们也可以是装饰性的并增加个性。

文本描边

许多卡通标题卡的字母都有粗体轮廓,使它们从背景中脱颖而出。我可以使用text-stroke来重现这种效果。很长一段时间以来,这个属性只能通过-webkit-前缀使用,但这也意味着它现在在现代浏览器中得到支持。

text-stroke是两个属性的简写。第一个,text-stroke-width,围绕单个字母绘制轮廓,而第二个,text-stroke-color,控制其颜色。对于“Whatever Goes Pup”,我在黄色文本上添加了4px的蓝色描边:

1
2
3
color: #eff0cd;
-webkit-text-stroke: 4px #7890b5;
text-stroke: 4px #7890b5;

当描边与阴影结合时尤其有用,因此对于“Growing, Growing, Gone”,我在一个几乎不模糊的1px阴影上添加了一个细的3px描边,以创建这种三维文本效果:

1
2
3
4
color: #fbb999;
text-shadow: 3px 5px 1px #5160b1;
-webkit-text-stroke: 3px #984336;
text-stroke: 3px #984336;

绘制顺序

使用text-stroke并不总是产生预期的结果,特别是对于较细的字母和较粗的描边,因为默认情况下浏览器会在填充上绘制描边。遗憾的是,CSS仍然不允许我像在Sketch中经常做的那样调整描边位置。然而,paint-order属性有一些值可以让我将描边放在填充后面,而不是前面。

paint-order: stroke首先绘制描边,然后绘制填充,而paint-order: fill则相反:

1
2
3
4
5
color: #fbb999;
paint-order: fill;
text-shadow: 3px 5px 1px #5160b1;
text-stroke-color:#984336;
text-stroke-width: 3px;

有效的描边保持字母的可读性,增加重量,并且——当与阴影和绘制顺序结合时——赋予平面文本真实的存在感。

文本内的背景

许多卡通标题卡超越了纯色,通过为字母添加纹理、渐变或插图细节。有时是纹理,其他时候可能是具有微妙色调变化的渐变。在网页上,我可以通过在文本后面使用背景图像或渐变,然后将其裁剪到字母的形状来重现这种效果。这依赖于两个属性协同工作:background-clip: text 和 text-fill-color: transparent。

首先,我在文本后面应用一个背景。这可以是位图或矢量图像,也可以是CSS渐变。对于来自《快手麦克劳》剧集“Baba Bait”的这个例子,标题文本包含一个从暗到亮的细微自上而下渐变:

1
background: linear-gradient(0deg, #667b6a, #1d271a);

接下来,我将该背景裁剪到字形并让文本透明,以便背景显示出来:

1
2
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;

仅用这两行,背景就不再绘制在文本后面;相反,它被绘制在文本内部。当与描边和阴影结合时,这种技术效果特别好。裁剪的渐变提供了字母的颜色和纹理,描边保持其边缘锐利,阴影将其从背景中抬起。它们共同使用一点CSS重现了手绘标题卡的分层外观。与往常一样,请仔细测试裁剪文本,因为浏览器怪癖有时会影响阴影和渲染。

将文本分割成单个字符

有时我不想样式化整个单词或标题。我想样式化单个字母——将一个字符微调到位置,给一个字形额外的重量,或者独立地动画化几个字母。

在纯HTML和CSS中,只有一种可靠的方法可以做到这一点:将每个字符包装在它自己的span元素中。我可以手动完成,但这会很脆弱,难以维护,并且在复制更改时会很快失效。相反,当我需要按字母控制时,我使用像splt.js这样的文本分割库(尽管其他解决方案也可用)。这需要一个文本节点并自动包装单词或字符,为我提供额外的钩子来动画化和样式化,而不会弄乱我的标记。

这是一种保持我的HTML可读和语义化的方法,同时为我提供了重现您在经典卡通标题卡中看到的不均匀、有个性的排版所需的精细控制。然而,这种方法有可访问性的注意事项,因为大多数屏幕阅读器按顺序读取文本节点。所以这样:

1
<h2>Hum Sweet Hum</h2>

…读起来如您所料: Hum Sweet Hum 但是这样:

1
2
3
4
5
6
<h2>
<span>H</span>
<span>u</span>
<span>m</span>
<!-- 等等 -->
</h2>

…可能因浏览器和屏幕阅读器而异而被不同解释。有些会连接字母并正确读出单词。其他可能在字母之间停顿,在最坏的情况下可能听起来像: “H…” “U…” “M…” 遗憾的是,一些分割解决方案并不能始终提供可访问的结果,因此我编写了自己的文本分割器splinter.js,目前处于测试阶段。

变换单个字母

要激活我的卡通文本分割器,我向要分割的元素添加一个data-属性:

1
<h2 data-split="toon">Hum Sweet Hum</h2>

首先,我的脚本将每个单词分离成单个字母,并将它们包装在一个应用了类和ARIA属性的span元素中:

1
2
3
<span class="toon-char" aria-hidden="true">H</span>
<span class="toon-char" aria-hidden="true">u</span>
<span class="toon-char" aria-hidden="true">m</span>

然后,脚本获取分割元素的初始内容,并将其作为aria属性添加,以帮助保持可访问性:

1
2
3
4
5
<h2 data-split="toon" aria-label="Hum Sweet Hum">
  <span class="toon-char" aria-hidden="true">H</span>
  <span class="toon-char" aria-hidden="true">u</span>
  <span class="toon-char" aria-hidden="true">m</span>
</h2>

应用了这些类属性后,我可以按自己的选择样式化单个字符。

例如,对于“Hum Sweet Hum”,我想复制其字母如何偏离基线。在使用我的卡通文本分割器后,我使用几个:nth-child选择器应用了四个不同的translate值来创建半随机外观:

1
2
3
4
5
6
7
8
/* 第4、8、12...个 */
.toon-char:nth-child(4n) { translate: 0 -8px; }
/* 第1、5、9...个 */
.toon-char:nth-child(4n+1) { translate: 0 -4px; }
/* 第2、6、10...个 */
.toon-char:nth-child(4n+2) { translate: 0 4px; }
/* 第3、7、11...个 */
.toon-char:nth-child(4n+3) { translate: 0 8px; }

但translate只是我可以用来变换卡通文本的一个属性。

我还可以旋转那些单个字符以获得更加混乱的外观:

1
2
3
4
5
6
7
8
/* 第4、8、12...个 */
.toon-line .toon-char:nth-child(4n) { rotate: -4deg; }
/* 第1、5、9...个 */
.toon-char:nth-child(4n+1) { rotate: -8deg; }
/* 第2、6、10...个 */
.toon-char:nth-child(4n+2) { rotate: 4deg; }
/* 第3、7、11...个 */
.toon-char:nth-child(4n+3) { rotate: 8deg; }

当然,我可以添加动画来晃动这些字符,使我的卡通文本风格标题变得生动。首先,我创建了一个旋转字符的关键帧动画:

1
2
3
4
5
6
@keyframes jiggle {
0%, 100% { transform: rotate(var(--base-rotate, 0deg)); }
25% { transform: rotate(calc(var(--base-rotate, 0deg) + 3deg)); }
50% { transform: rotate(calc(var(--base-rotate, 0deg) - 2deg)); }
75% { transform: rotate(calc(var(--base-rotate, 0deg) + 1deg)); }
}

然后将其应用由我的卡通文本分割器创建的span元素:

1
2
3
.toon-char {
animation: jiggle 3s infinite ease-in-out;
transform-origin: center bottom; }

最后,设置旋转量和每个字符开始晃动前的延迟:

1
2
3
4
5
6
7
8
9
.toon-char:nth-child(4n) { --base-rotate: -2deg; }
.toon-char:nth-child(4n+1) { --base-rotate: -4deg; }
.toon-char:nth-child(4n+2) { --base-rotate: 2deg; }
.toon-char:nth-child(4n+3) { --base-rotate: 4deg; }

.toon-char:nth-child(4n) { animation-delay: 0.1s; }
.toon-char:nth-child(4n+1) { animation-delay: 0.3s; }
.toon-char:nth-child(4n+2) { animation-delay: 0.5s; }
.toon-char:nth-child(4n+3) { animation-delay: 0.7s; }

一帧留下印象

卡通标题艺术家有一帧来留下印象,他们的排版和他们绘制的艺术作品一样重要。在网页上也是如此。

一个设计良好的标题或英雄区域需要清晰度、个性和自信——而不仅仅是一个褪色的全宽背景图像。

通过一些精心选择的CSS属性——阴影、描边、裁剪背景和一些克制的动画——我们可以重现同样的冲击力。我喜爱卡通文本不是因为我怀旧,而是因为它的设计是刻意的。做出深思熟虑的选择,让一点卡通文本排版为您的设计增添冲击力。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计