在CSS3和SVG出现之前,我们脑海里可能会有一些酷炫的想法,因为技术的不支持一直没办法实现。但是,随着技术的发展,这些高大上的想法越来越有实现的可能。例如,我们现在有很多的方式来实现文字填充动画。

大概一年前,Sara Soueidan发表了一篇Techniques for Creating Textured Text的文章。这篇神奇的文章告诉我们如何使用一些现代web技术、包括canvas等,来实现创建纹理文字的效果。如图:

看到这个,我们就会想,能不能让里面文字填充的背景动起来?我们可以实现文字的阴影或渐变的动画?或者使用一个视频(video)来填充文本如何?

这篇文章我将会分享如何创建图案填充的文本动画。当然,最好还要保留文本的文字选择能力,用于复制、粘贴等。

因为是高度实验性的代码,可能有大部分浏览器不支持,为了更好的体验效果请用chrome最新版浏览器来阅读该文章,谢谢。

方法1:使用background-clip

这个方式可能是我们脑海里第一个蹦出来的想法,虽然只有WebKit内核的浏览器支持,所以要加上-webkit-前缀。

html标签如下:

1
2
3
<div class="box-with-text">
Text
</div>

CSS是这样的:

1
2
3
4
5
.box-with-text{
background-image:url('img/1.jpg');
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
}

效果如下:

Text

文字依然文本,所以我们可以选择和复制。其缺点是缺乏浏览器的支持。

但我们可以用CSS渐变与-webkit-前缀以除去非Webkit的浏览器的背景:

1
2
3
4
5
6
.box-with-text{
background-image:-webkit-linear-gradient(transparent, transparent),
url('img/1.jpg');
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
}

不支持的浏览器将完全忽略了整个字符串,我们能够避免完全显示的背景图像。这简单的一招将使我们能够解决非Webkit内核的浏览器,如果我们不希望显示的图像话。

如果我们要实现填充动画,CSS可以实现。但是我们CSS动画只能实现改变背景的位置和大小,不能平滑地改变颜色。效果如图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// scss
@include keyframes(stripes) {
100% {
background-position: 0 -50px;
}
}

.box-with-text{
background-image: -webkit-linear-gradient(crimson 50%, #FD0 50%);
background-repeat: repeat;
background-position: 0 0;
background-size: 100% 50px;
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
@include animation(stripes 2s linear infinite);
}
Text

结论

使用该技术实现:只支持WebKit内核浏览器,有限的填充文字的动画效果。

方法2:使用SVG

SVG是一种奇妙的格式,它具有良好的浏览器支持。在SVG中,我们有三种方式来使文章填充图案:

  • fill
  • mask
  • clip-path

我将会逐一使用这些方式来展示文字填充图案的效果。

如果你不了解SVG,建议你阅读SVG规范,或者其他SVG教程等。

html使用方法如下:

1
2
3
4
5
<svg viewBox="0 0 600 300">
<text text-anchor="middle" x="50%" y="50%" dy=".35em" class="text">
Text
</text>
</svg>

在它的常规文本中,我们也可以自由的复制粘帖

Text

在SVG中添加一个简单的linearGradient标签:

1
2
3
4
5
6
7
8
9
10
11
<svg viewBox="0 0 600 300">
<!-- Gradient -->
<linearGradient id="gr-simple" x1="0" y1="0" x2="100%" y2="100%">
<stop stop-color="hsl(50, 100%, 70%)" offset="10%"/>
<stop stop-color="hsl(320, 100%, 50%)" offset="90%"/>
</linearGradient>
<!-- text -->
<text text-anchor="middle" x="50%" y="50%" dy=".35em" class="text">
Text
</text>
</svg>
1
2
3
4
5
.text {
font: 12.5em/1 Open Sans, Impact;
text-transform: uppercase;
fill: url(#gr-simple);
}

就可以实现渐变填充的效果:

Text

想了解更多关于gradients的知识可以阅读Joni Trythall的文章Getting Started with SVG Gradients

使用fill来填充颜色的方式也可以直接写在text的属性fill上,要使用gradients是必须定义ID,并且将之插入到url()中。

1
2
3
4
5
<svg viewBox="0 0 600 300">
<text text-anchor="middle" x="50%" y="50%" dy=".35em" class="text" fill="url(#gr-simple)">
Text
</text>
</svg>

当然,你喜欢使用css的话也行。

1
2
3
.text {
fill: url(#gr-simple);
}

我们可以设置gradient的字体颜色,但是我们需要使用SMIL来控制gradient的其他属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- Gradient -->
<radialGradient id="gr-radial"
cx="50%" cy="50%" r="70%">

<!-- Animation for radius of gradient -->
<animate attributeName="r"
values="0%;150%;100%;0%"
dur="5s" repeatCount="indefinite" />

<!-- Animation for colors of stop-color -->
<stop stop-color="#FFF" offset="0">
<animate attributeName="stop-color"
values="#333;#FFF;#FFF;#333"
dur="5s" repeatCount="indefinite" />
</stop>
<stop stop-color="rgba(55,55,55,0)" offset="100%"/>
</radialGradient>

效果如下

Text

想了解更多关于SMIL的知识可以阅读Sara Soueidan的文章A Guide to SVG Animations (SMIL)

现在我们来使用<pattern>试试。

Text

<pattern>标签的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
<pattern id="p-spots" viewBox="0 0 80 80" patternUnits="userSpaceOnUse" width="60" height="60" x="5" y="5" >
<g class="g-spots">
<circle r="5" cx="10" cy="10"/>
<circle r="7" cx="30" cy="30"/>
<circle r="5" cx="50" cy="10"/>
<circle r="9" cx="70" cy="30"/>
<circle r="11" cx="50" cy="50"/>
<circle r="5" cx="10" cy="50"/>
<circle r="7" cx="30" cy="70"/>
<circle r="9" cx="70" cy="70"/>
</g>
</pattern>

这里仅定义了圆的大小和位置,样式需要在css中定义。或者使用sass:

1
2
3
4
5
6
7
8
9
10
11
//scss
$colors: #1D4259, #0A7373, #30BF7C, #BAF266, #EEF272;
$max: length($colors);

.g-spots circle {
@for $item from 1 through $max {
&:nth-child(#{$max}n + #{$item}){
fill: nth($colors, $item);
}
}
}

这样就会生成下面的css:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.g-spots circle:nth-child(5n + 1) {
fill: #1D4259;
}
.g-spots circle:nth-child(5n + 2) {
fill: #0A7373;
}
.g-spots circle:nth-child(5n + 3) {
fill: #30BF7C;
}
.g-spots circle:nth-child(5n + 4) {
fill: #BAF266;
}
.g-spots circle:nth-child(5n + 5) {
fill: #EEF272;
}

想了解更多有关于SVG patterns的,可以阅读MDN-SVG教程相关文章。

我是使用sass来做css的预编译的,所以可以很方便的指定动画的延时。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// scss
$colors: #551F7A, #BA2799, #D9587A, #FFDD00, #FFF3A1;
$max: length($colors);

$time: 2s;
$time-step: $time/$max;

.g-stars polygon {
stroke-width: 0;
animation: stroke $time infinite;

@for $item from 1 through $max {
&:nth-child(#{$max}n + #{$item}){
$color: nth($colors, $item);
fill: $color;
stroke: $color;
animation-delay: -($time-step*$item);
}
}
}

@keyframes stroke {
50% {
stroke-width: 10;
}
}

// 编译后的css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// css
.g-stars polygon {
stroke-width: 0;
animation: stroke 2s infinite;
}
.g-stars polygon:nth-child(5n + 1) {
fill: #551F7A;
stroke: #551F7A;
animation-delay: -0.4s;
}
.g-stars polygon:nth-child(5n + 2) {
fill: #BA2799;
stroke: #BA2799;
animation-delay: -0.8s;
}

.g-stars polygon:nth-child(5n + 3) {
fill: #D9587A;
stroke: #D9587A;
animation-delay: -1.2s;
}
.g-stars polygon:nth-child(5n + 4) {
fill: #FFDD00;
stroke: #FFDD00;
animation-delay: -1.6s;
}
.g-stars polygon:nth-child(5n + 5) {
fill: #FFF3A1;
stroke: #FFF3A1;
animation-delay: -2s;
}

@keyframes stroke {
50% {
stroke-width: 10;
}
}

接下来我们把里面的圆圈替换成星星,并且让他放大缩小。

Text

因为代码比较长,就不放出了,大家可以自己审查元素来查看。

当然我们也可以使用gif动画,虽然体积会比较庞大,但是可以创建出很好的效果。

Text

pattern中用来使用图片的方法如下:

1
2
3
<pattern id="p-fire" viewBox="30 100 186 200" patternUnits="userSpaceOnUse" width="216" height="200" x="-70" y="35" >
<image xlink:href="/img/post/AnimateTextFills/fire.gif" width="256" height="300"/>
</pattern>

不同于background-clip,文本文字将正确的显示在最新的浏览器。

接下来我们来谈谈stroke,对于html来说,我们可以使用text-shadow的方式来模拟一个类stroke,但是这种方式很大的局限性。

如图,实现这样的效果可以使用如下代码:

1
2
3
4
5
.box-with-text {
text-shadow: -0px -0px 0 yellowgreen, 0px -0px 0 yellowgreen, 0px 0px 0 yellowgreen, -0px 0px 0 yellowgreen, -1px -1px 0 yellowgreen, 1px -1px 0 yellowgreen, 1px 1px 0 yellowgreen, -1px 1px 0 yellowgreen, -2px -2px 0 yellowgreen, 2px -2px 0 yellowgreen, 2px 2px 0 yellowgreen, -2px 2px 0 yellowgreen, 0 5px 15px rgba(0, 0, 0, 0.2);
font: 26vmax/.85 Open Sans, Impact;
color: #FFF;
}

用text-shadow实现起来确实非常的繁琐,而且代码相当的臃肿。

不同于HTML,stroke是可用于SVG的所有元素,包括文本。并且除了作为线条它们也可填充渐变和图案。

Text

对于这种类型的stroke,我们需要把许多颜色置入到文本中,可以使用下面的方法来实现。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div class="demo">
<svg viewBox="0 0 600 300" class="demo6">
<symbol id="s-text">
<text text-anchor="middle" x="50%" y="50%" dy=".35em">
Text
</text>
</symbol>
<!-- 复制多个文本 -->
<use xlink:href="#s-text" class="text"></use>
<use xlink:href="#s-text" class="text"></use>
<use xlink:href="#s-text" class="text"></use>
<use xlink:href="#s-text" class="text"></use>
<use xlink:href="#s-text" class="text"></use>
</svg>
</div>

用样式控制颜色和动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$colors: #551F7A, #BA2799, #D9587A, #FFDD00, #FFF3A1;
$max: length($colors);

@include keyframes(stroke2) {
100% {
stroke-dashoffset: -400;
}
}

//scss
.text {
fill: none;
stroke-width: 6;
stroke-linejoin: round;
stroke-dasharray: 70 330;
stroke-dashoffset: 0;
@include animation(stroke2 6s infinite linear);
@for $item from 1 through $max {
&:nth-child(#{$max}n + #{$item}){
$color: nth($colors, $item);
stroke: $color;
@include animation-delay(#{-(1.2 * $item)}s);
}
}
}

编译出来的css就是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
.text {
fill: none;
stroke-width: 6;
stroke-linejoin: round;
stroke-dasharray: 70 330;
stroke-dashoffset: 0;
animation: stroke2 6s infinite linear;
}

.text:nth-child(5n + 1) {
stroke: #551F7A;
animation-delay: -1.2s;
}

.text:nth-child(5n + 2) {
stroke: #BA2799;
animation-delay: -2.4s;
}

.text:nth-child(5n + 3) {
stroke: #D9587A;
animation-delay: -3.6s;
}

.text:nth-child(5n + 4) {
stroke: #FFDD00;
animation-delay: -4.8s;
}

.text:nth-child(5n + 5) {
stroke: #FFF3A1;
animation-delay: -6s;
}

@keyframes stroke {
100% {
stroke-dashoffset: -400;
}
}

对于每一个stroke,我们单独设置一个动画延时,所有他们的路径就不会积聚在同一个地方,而是通过在一个字母上不同的位置循环。

结论

使用这种方法的文本可以使用CSS样式。我们也可以选择和复制的文本。另一大优势是,SVG具有非常好的浏览器的支持。因此,创建这类文本,SVG是最好的人选之一。


未完待续……