CSS秘密花园:条纹背景

发布于 大漠

CSS Secrets》是@Lea Verou最新著作,这本书讲解了有关于CSS中一些小秘密。是一本CSSer值得一读的一本书,经过一段时间的阅读,我、@南北@彦子一起将在W3cplus发布一系列相关的读后感,与大家一起分享。

CSS Secrets

问题

和其他视觉设计相关的媒体一样,在 Web 上各类大小、颜色、角度不同的纹理也非常流行。不过,实现这些纹理的技术却并不理想。通常,我们需要创建独立的位图,如果有需求变更的话都需要重新更改文件。有些开发者使用 SVG 替代位图,但是 SVG 仍然是一种独立的文件,而且其语法也不够友好。那么是否有一种出色的方法让我们直接在 CSS 中创建纹理呢?你会惊喜的发现,我们将在下面的介绍中逐步解决这一问题。

解决方案

首先,假设我们需要一个简单地垂直渐变,颜色从 #fb3#58a

条纹背景

图1注:我们初始化的渐变效果

background: linear-gradient(#fb3, #58a);

现在,让我们将两种颜色的过渡点调的更近一些:

条纹背景

图2注:现在渐变占据了整体60%的高度,其余的部分都是纯色;颜色过渡点的位置在这里使用虚线标识出来

background: linear-gradient(#fb3 20%, #58a 80%);

到此为止,容器顶部20%的部分是纯粹的 #fb3,底部20%的部分是纯粹的 #58a,所以实际上渐变的部分只占有了容器的 60%。那么如果我们将颜色过渡点(color stops)调整地更近一些(比如 40%60%,如下图所示),那么渐变的部分就会更小了。这就让我们很自然地联想到,如果颜色过渡点相同会发生什么呢?

条纹背景

图3注:现在渐变占据了整体20%的高度,其余的部分都是纯色;颜色过渡点的位置在这里使用虚线标识出来

background: linear-gradient(#fb3 50%, #58a 50%);

"如果多个颜色过渡点的位置相同,那么就会在两个颜色之间生成一个无限小的过渡。实际效果就是,一个颜色不再会流畅地过渡到下一个颜色了,而是会突然变成下一个颜色。" — CSS Image Values Level 3

正如下图中看到的那样,已经看不到颜色过渡区域了,只有两种纯色,每种纯色占有容器一半的高度。可以说,我们已经创建了两个宽大的水平纹理。

条纹背景

图4注:现在两个颜色的过渡点位置重合了

因为渐变本质上就是 backgroud-image,所以我们可以像对待 background-image 一样使用 background-size 调整大小:

background: linear-gradient(#fb3 50%, #58a 50%);
background-size: 100% 30px;

就像下图看到的那样

条纹背景

图5注:我们生成的背景没有使用平铺

我们将两条纹理都缩小到了 15px 的高度。因为背景是可以平铺的,所以我们可以让整个容器填充这种水平纹理了:

条纹背景

图6注:最终的水平渐变

当然,我们还可以创建不等宽的条纹,秘诀就是调节一下颜色过渡点的位置:

background: linear-gradient(#fb3 30%, #58a 30%);
background-size: 100% 30px;

为了避免每次需求变更都需要修改两处颜色过渡点的重复工作,我们可以充分利用规范中介绍的原理:

“如果某个颜色过渡点的位置小于它之前的颜色过渡点,那么该颜色过渡点的位置就会被重置为所有在它前面的颜色过渡点的最大位置。” — CSS Images Level 3

这意味着如果我们将第二个颜色过渡点设置为0,那么它实际的位置就会被浏览器重置为它前面颜色过渡点的最大位置,而这个位置恰恰就是我们需要的过渡位置。因此,下面的代码不仅仅是和下图 具有同样的效果,而且更加简洁、更具有可维护性:

条纹背景

图7注:不等宽条纹

background: linear-gradient(#fb3 30%, #58a 0);
background-size: 100% 30px;

创建更多颜色的纹理和创建两种颜色的问题同样简单。比如,下面的代码块创建了三种颜色的纹理:

条纹背景

图8注:三色条纹

background: linear-gradient(#fb3 33.3%,
            #58a 0, #58a 66.6%, yellowgreen 0);
background-size: 100% 45px;

垂直纹理

水平纹理非常容器创建,但是在 Web 上并不是所有的纹理都是水平的,此外还有很多纹理是垂直的

条纹背景

图9注:垂直条纹,上面:背景没有使用平铺;下面:平铺之后的条纹

而且,在视觉上最受欢迎的纹理可能是倾斜的纹理。值得庆幸的是,CSS 的渐变可以帮助我们实现这样的效果,只是实现的难度各种不同。

实现垂直纹理的代码和水平纹理的非常相似,只有一个主要的差异:一个指定渐变方向的参数。我们可以通过指定这个参数来创建水平纹理,不过对于此次要创建的垂直纹理,使用它的默认值即可(to bottom)。此外,同样需要创建一个不同的 background-size,原因很明显:

background: linear-gradient(to right, /* or 90deg */
              #fb3 50%, #58a 0);
background-size: 30px 100%;

斜纹

在实现了水平纹理和垂直纹理之后,我们可能会尝试通过 background-size 和渐变方向来实现倾斜的纹理(45°),就像这样的代码:

background: linear-gradient(45deg,
              #fb3 50%, #58a 0);
background-size: 30px 30px;

不过,就像下图所示,效果非常不好。

条纹背景

图10注:第一次创建斜纹的失败效果

究其原因,就是因为我们只是将每一条纹理旋转了 45°,旋转的并不是图形整体。让我们回忆一下使用位图创建斜纹的方法,其中引入的位图和下图相类似。

条纹背景

图11注:这就是创建斜纹的拼接图,看起来是不是很像?

它包含了四条纹理,而不是这里的两条,所以看起来像是无缝连接的。这就是我们需要在 CSS 中重新创建的纹理,所以我们需要更多的颜色过渡点:

background: linear-gradient(45deg,
              #fb3 25%, #58a 0, #58a 50%,
              #fb3 0, #fb3 75%, #58a 0);
background-size: 30px 30px;

完成后的效果:

条纹背景

图12注:45° 斜纹;虚线用来标识复用的区块

如你所见,虽然我们成功的创建了斜纹,但是看起来比水平和垂直纹理要窄一些。为了回答这个问题,我们需要使用学校里学到的勾股定理来计算直角三角形的各边变长。勾股定理指出,最长边等于其他两边的平方和。在直接三角形中,两条短边相等所以么最长边就等于:

在我们创建的这个斜纹中,background-size 指定的就是三角形最长边的边长,因此纹理的宽度就是直角边的长度。你可以看下图,获得更清晰的解释。

条纹背景

图13注:大小为 20pxbackground-size 将会生成宽度为 的条纹

这就是说,如果想要获得原来 15px 的宽度,就需要将 background-size 指定为

background: linear-gradient(45deg,
              #fb3 25%, #58a 0, #58a 50%,
              #fb3 0, #fb3 75%, #58a 0);
background-size: 42.426406871px 42.426406871px;

最终效果:

条纹背景

图14注:45°斜纹的最终效果;值得注意的是,现在条纹的宽度和其示例中条纹的宽度一致了

不过,除非某人拿枪威胁你非得让斜纹宽度为 15px(这种情况你你死定了,因为根号二是一个有理数,所以这里虽然使用了一个高精度的数值,但仍然不是它的值),否则我建议你可以约取一个数值,比如 42.4px 或者 42px

更出色的斜纹

上一节介绍的方法使用起来不够灵活。如果我们让斜纹倾斜 60°30°3.1415926535° 时又该怎么处理呢?如果我们只是想修改一下角度,效果看起来还是不错的(下图是一个失败的尝试)。

条纹背景

图15注:失败的 60° 斜纹

所幸的是,有一种更好的方式创建斜纹。很少人知道的是,其实 linear-gradient()radial-gradient()也有一个实现平铺效果的版本:repeating-linear-gradient()repeating-radial-gradient()。它们的原理基本相同,只有一个差别:颜色过渡点也可以无限平铺,知道填充整个图片。所以,可以这样平铺渐变:

条纹背景

图16注:平铺后的线性渐变

background: repeating-linear-gradient(45deg,
              #fb3, #58a 30px);

这段代码与下面的线性渐变代码等效:

background: linear-gradient(45deg,
              #fb3, #58a 30px,
              #fb3 30px, #58a 60px,
              #fb3 60px, #58a 90px,
              #fb3 90px, #58a 120px,
              #fb3 120px, #58a 150px, ...);

你可能已经猜到了,平铺线性渐变同样适用于斜纹。因为它们的平铺特性,这意味我们的整个背景都可以使用渐变图片来实现了。因此,我们无需再担心创建平铺小图片的无缝连接问题了。

为了做一下比较,下图中的背景也可以使用下面的平铺渐变来实现:

条纹背景

图17注:60° 条纹

background: repeating-linear-gradient(45deg,
              #fb3, #fb3 15px, #58a 0, #58a 30px);

这种方法最明显的优势就是减少了重复:如果需要修改颜色,那么只需修改两处即可完成,而之前则需要三次。另一个值得注意的地方在于,现在我们可以使用颜色过渡点来控制颜色的宽度,而不再使用 background-size,background-size 专注于控制元素的尺寸。这意味着定义条纹的宽度会更加直接,无需再计算 根号二这样的数了!

不过,最大的好处还在于现在可以任意调整倾斜的角度了,而且无需再考虑纹理块之间的无缝连接问题。比如,这里有一个 60° 的条纹示例:

条纹背景

background: repeating-linear-gradient(60deg,
              #fb3, #fb3 15px, #58a 0, #58a 30px);

简单到只需调节角度即可实现!值得注意的是,使用这种方法创建两条斜纹的话,我们需要设置四个颜色过渡点。这意味着,使用第一种方式创建水平和垂直纹理更方便,而使用这种方式更适合于创建斜纹。如果我们要创建 45°的斜纹,那么可以组合使用这两种方法,核心是使用 repeating-linear-gradient 来缩简重复代码:

background: repeating-linear-gradient(45deg,
              #fb3 0, #fb3 25%, #58a 0, #58a 50%);
background-size: 42.426406871px 42.426406871px;

展望:拥有两个位置的颜色过渡点 在不久的将来,我们将可以为同一个颜色过渡点设定两个位置,现在对此已经有了一个简单地规划,并添加进了 CSS Image Values Level 4。在这里,可以将新语法看成一个简写形式,只需要添加两个颜色过渡点以及不同的过渡位置,就可以创建一个纹理。举个例子来说,我们可以将上图的斜纹代码修改为: background: repeating-linear-gradient(60deg, #fb3 0 15px, #58a 0 30px); 这样的做不仅让代码更加容易理解,而且也更具有可维护性:不再需要重复定义过渡点颜色,只需编写一次即可实现纹理。不幸的是,在本书编写的时候,还没有任何浏览器支持它。

灵活可扩展的精致斜纹

通常来说,我们的斜纹并不是由多种不同颜色组成的,往往只是一种颜色的变化就可以构造成精致的纹理。比如,看看下面这个纹理:

background: repeating-linear-gradient(30deg,
              #79b, #79b 15px, #58a 0, #58a 30px);

具体效果如下:

条纹背景

图18注:添加了细微亮色的条纹

你可以看到,组成该纹理的是同一色彩衍生的两种颜色,其中一种比另一种的亮度更高而已。不过,我们无法从代码中分辨出两个颜色之间的关系。此外,如果我们想改变色彩,那么至少需要在四个地方进行编辑。

所幸的是,这里有一个更好的编写方式:不再为纹理指定具体的颜色,只使用深色定义 background-color,然后它上面添加一个半透明白色的纹理:

background: #58a;
background-image: repeating-linear-gradient(30deg,
                    hsla(0,0%,100%,.1),
                    hsla(0,0%,100%,.1) 15px,
                    transparent 0, transparent 30px);

最终的结果上图一模一样,但我们现在只需编辑一处就可以修改成不同的色彩。使用这个方法,我们甚至还得到了一个额外的好处,那就是为低版本浏览器提供了降级处理,当浏览器不支持渐变时,就会显示深色背景。在下一节中,我们将会看到,使用透明区域的渐变图案,可以让我们创建更复杂的图案。

扩展阅读

总结

这一节从CSS3的线性渐变开始,详细介绍了如何通过线性渐变配合background-size实现双色、多色斑马纹理背景效果。而且将线性渐变的方向换成角度值,可以实现斜纹纹理背景。当然,事实上,不同的背景(渐变做的背景)配合background-size可以实现更多的纹理背景。感兴趣的同学,看完之后可以参阅文章中的扩展资源,了解更多相关知识。