CSS3 实现纹理背景的通用解法

背景

最近在学习CSS新世界这本书(张鑫旭大神的书,看了绝对不后悔),看到讲解了background属性的多背景特性,在讲解例子时提到了这个网站: projects.verou.me/css3pattern…

里面是一些比较有特点的CSS纹理背景的demo展示:

image.png

网站里的demo都挺有意思的,自己写代码实现了几个demo案例,感觉也挺有趣。于是就将学习思路整理一下分享给大家,提供一些解题思路。

预备知识

如果你已经对CSS渐变有了一定的理解,可以直接跳到下一节看案例分析

CSS3制作纹理背景主要涉及到CSS3的几个特性:

background-image的多背景特性

background-image 属性可以为一个元素设置一个或多个背景图像。绘制时图像以 z轴 方向堆叠进行。先指定的图像会在后指定的图像上面绘制,因此指定的第一个图像是最接近用户的

MDN上展示的例子如下:

.example1 {
  width: 400px;
  height: 200px;
  background-image: url("https://interactive-examples.mdn.mozilla.net/media/examples/lizard.png"),
                    url("https://interactive-examples.mdn.mozilla.net/media/examples/star.png");
}
复制代码

效果: image.png

除此之外,background-color 会在图像之下绘制,元素的边框border会在图像之上绘制,整体的顺序如下:

image.png

之所以可以使用CSS生成丰富的纹理背景,都要归功于background-image的多背景特性,可以说没有这个特性就无法达到我们想要的效果。

利用渐变绘制图像

CSS3支持渐变函数,可以利用他们绘制出多种多样的图像。常用的渐变函数有:

  • linear-gradient()repeating-linear-gradient()
  • radial-gradient()repeating-radial-gradient()
  • conic-gradient()repeating-conic-gradient()

下面简单介绍下这些渐变函数。

linear-gradient() 和 repeating-linear-gradient()

即线性渐变与重复线性渐变。linear-gradient() 可以用来创建一个表示两种或多种颜色线性渐变的图片。

关于线性渐变的构成原理这里不再特别说明,可参见MDN 线性渐变的构成 一节

在这里我们主要应用的一点特性是:

如果两个或多个颜色终止在同一位置,则在该位置声明的第一个颜色和最后一个颜色之间的过渡将是一条生硬线。

利用这条特性,可以生成颜色界限分明的色块

普通的渐变,颜色间会进行自然地过渡:

background: linear-gradient(45deg, deepskyblue, 50%, pink);
复制代码

image.png

利用上述特性:

background: linear-gradient(45deg, deepskyblue 50%, pink 50%);
复制代码

将生成颜色边界分明而不是过渡效果的色块:

image.png

灵活运用这点,就可以绘制出各种各样具有清晰直线边界的图形

repeating-linear-gradient() 类似于linear-gradient() 函数,它用来创建一个由重复线性渐变组成的图形。当渐变图案无法覆盖整个容器时,该函数会将图案重复以覆盖整个容器,例如:

background: repeating-linear-gradient(45deg, deepskyblue 0, deepskyblue 50px, pink 50px, pink 100px);
复制代码

如果是普通的线性渐变,图片会在100px后全部显示为pink;在重复线性渐变中,则会将这个图案铺满容器:

image.png

radial-gradient() 和 repeating-radial-gradient()

即径向渐变与重复径向渐变。区别于上文的线性渐变,径向渐变是由从原点发出的两种或者多种颜色之间的逐步过渡组成。它的形状可以是圆形或椭圆形。

普通的渐变:

background: radial-gradient(deepskyblue, pink);
复制代码

效果:

image.png

同样,径向渐变也可以生成颜色边界分明而不是过渡效果的色块:

background: radial-gradient(deepskyblue 50%, pink 50%);
复制代码

image.png

灵活运用这点,就可以绘制出各种各样具有清晰边界的椭圆or圆、圆环

同样的,径向渐变也有重复函数, repeating-radial-gradient() 可以创建重复的径向渐变图案来填满容器,例如:

background: repeating-radial-gradient(deepskyblue 0, deepskyblue 40px, pink 40px, pink 80px);
复制代码

效果如下:

image.png

conic-gradient 和 repeating-conic-gradient

最后的渐变叫做锥形渐变。之所以叫锥形渐变,是因为生成的图案很像圆锥体的俯视图(开始回忆一些高中立体几何知识……)。典型的应用是生成色轮。

简单的渐变:

background: conic-gradient(deepskyblue, pink, green);
复制代码

效果:

image.png

同样的,锥形渐变的边界也可以生成颜色边界分明的色块:

background: conic-gradient(deepskyblue 33%,pink 33% 66%, green 66% 100%);
复制代码

image.png

利用锥形渐变就可以生成固定辐射角度的色块。

锥形渐变的重复函数, repeating-conic-gradient() 可以创建重复的锥形渐变图案来填满容器,例如:

background: repeating-conic-gradient(deepskyblue 0 30deg, pink 30deg 60deg);
复制代码

效果:

image.png

到这里我们的准备工作就算是结束了,需要了解的前置知识已经准备好,下一步就可以开始绘制纹理了。

第一步:观察纹理,找到重复单元

要用CSS做一个纹理,第一步就是要观察纹理。

这一步我们要找到纹理中的最小可重复元素。这一步很重要,如果找不对那么后面生成的纹理就无法达到预期。

我总结了几个观察时要注意的点:

  • 观察纹理中出现的线条,直线还是曲线。不同的线条需要使用不同的渐变函数来生成。
  • 找到的可重复元素一定要是最小的。如果不是最小单元,必然会做很多多余的工作;
  • 可重复单元一定是矩形

接下来我们结合具体案例来分析一下。以下面的Zig-Zag为例:

image.png

那么首先我们分析纹理中出现的线条。很显然,纹理中出现的线条都是直线。预备章节中,哪些渐变函数能够生成直线呢?很显然是线性渐变

接下来我们要寻找最小的可重复元素。这个纹理比较简单,很直观就能看出,最小单元为:

image.png

整个纹理都可以由这个最小单元重复得来。

到这里可能有人要问,为什么最小单元不是这个:

image.png

虽然但是,别忘了最重要的一点:可重复单元一定是个矩形。CSS中画不出这种不规则图形啊(哪怕是背景透明的圆形,占据的空间也一定是矩形空间)。

插个题外话,之前在群里有群友问如何将一张png图片的背景去掉,让图片的边界就是颜色的边界。这是无法实现的,无论是图片,还是CSS元素,必然都是矩形的,切记不要因为背景透明就把颜色边界当作图片边界。

在这个步骤中,我们将制作纹理想象成铺地砖就好了,地砖就是最小的可重复单元,我们的目标就是确定地砖的样式。

这一步骤完成后,基本上我们的工作已经完成了70%。

第二步:用背景图片和背景定位组合绘制最小单元

上一步我们确定了基本单元的样式,这一步我们需要用background属性来实现这个样式。

还记得我们观察纹理的第一个注意点吗:

观察纹理中出现的线条,直线还是曲线。

如果纹理线条为直线,则基本上要用到的是线性渐变函数linear-gradient();如果是曲线,那么大概率就是径向渐变函数gradial-gradient()。锥形渐变和渐变的重复函数则是依据具体的case来看,用到的概率相对来说较小。

上面的例子很显然,线条都是直线,所以这里使用linear-gradient()就可以了。

确定了绘制要使用的渐变函数后,继续观察我们的重复单元,可以发现,只绘制一个背景图是无法生成这个图案的。

很显然,这里需要多个不同的背景元素进行位移重叠的操作后才能合成。这里利用的是background 属性的多背景特性,再配合background-position属性对每个背景图案进行定位就可以达成效果。

那么我们现在开始进行纹理的绘制。

首先可以确定的是背景元素的尺寸,这里只要将background-size的宽高设置相同即可:

background-size: 30px 30px;
复制代码

接下来就可以开始对背景中的每个图案进行单独绘制。

要绘制基本单元,我们要继续将基本单元拆解成更加简单的图形。这里提供一些思路:

  • 容易绘制的元素作为图形绘制,不好绘制的尽量使用背景颜色填充。
  • 图案中的直线,一般使用线性渐变,基本图形一般为三角形、矩形
  • 图案中的曲线,多数使用径向渐变,基本图形一般是圆形、椭圆、有一定弧度的扇形or圆环

zig-zag这个例子中所有的线条都是直线,因此用来组合元素的基本图形就是三角形。将图形中的三角形进行拆分后,我们可以得到四个三角形:

image.png

(画图手抖,凑合看看)

可不要把上面的3+4直角三角形当作一整个基本图形,CSS背景是无法旋转的。

可以看出下边的1和2很好写,很明显是 45度的红色到浅色背景色的线性渐变,以及-45度的线性渐变,渐变断点位置都在四分之一,也就是25%处,背景定位无需更改,在原点即可。

浅色部分就直接作为背景色,这样我们可以写出包含1和2以及背景颜色的代码:

background: linear-gradient(45deg, #EC173A 25%, transparent 25%) 0 0,
            linear-gradient(135deg, transparent 75%, #EC173A 75%) 0 0,
            #ECEDDC;
background-size: 30px 30px;
复制代码

渐变的起始颜色和终点颜色,以及角度、位置,都不是只有一种写法的。以2号为例,可以写成-45度,红色在25%的位置到背景色的渐变,也可以写成135度背景色在75%的位置到红色的渐变。按照个人习惯书写即可。

而3号和4号显然不是在原点定位的。我们将3号所在的背景元素补全, 发现应该是位移过的背景:

image.png

灰色矩形部分就是3号三角形所处的背景元素位置,是-15px 0。同理我们可以得到右侧4号三角形的位置是15px 0

综合上面的四个背景元素,可以得到CSS代码:

整个过程我们用一个动画来解释:

2022-06-15 14.55.47.gif

于是这个纹理我们就做好了。

留个简单的课后题,大家是否还有别的写法?比如以红色为背景颜色,将浅色部分拆分呢?

进阶案例

上面分析的例子相对来说线条简单,比较容易拆解,不需要考虑多个背景间的层级关系,只需要简单的拼合即可。

下面我们再分析一些比较复杂的纹理。

箭头纹理

image.png

这个纹理一眼看过去是倾斜四十五度铺满的箭头图案,并且具有两种不同的颜色,因此我们的基本重复单元应该是下面两种方案:

image.png image.png

我这里以右侧的方案2为基准。

观察了一下图形,首先按照之前的三角形原则,可以拆分出两个图案:

image.png

先把这部分的代码写出来,基本单元以128px * 128px为准:

background: linear-gradient(45deg, #92baac 25%, transparent 25%),
            linear-gradient(45deg, #92baac 25%, transparent 25%) 64px -64px,
background-color: #e1ebbd;
background-size: 128px 128px;
复制代码

接下来考虑如何组合剩余的图案。箭头的主体部分是个倾斜45度的正方形,很显然我们无法直接用渐变达成。

这时我们可以考虑通过多背景的层级覆盖来达成最终效果,比如将下面的部分作为整体,放置在多个背景的最底层

image.png

background: linear-gradient(45deg, #92baac 25%, transparent 25%),
            linear-gradient(45deg, #92baac 25%, transparent 25%) 64px -64px,
            linear-gradient(45deg, transparent 50%, #e1ebbd 50%, #e1ebbd 75%, transparent 75%),
background-color: #e1ebbd;
background-size: 128px 128px;
复制代码

效果变成:

image.png

最后我们就剩下箭头主体以及两个边角的小三角部分:

image.png

如果想要单独制作这样的几个元素,需要写大量的渐变来合成。我们不如转换思路,制作更加完整的背景来进行叠加:

image.png

此处我们制作-45deg的渐变,从画面的右下角渐变到左上角,并将这个图形放在背景的最底层。其中比较重要的几个颜色断点位置通过百分比来显示效果更好:

记住当前图层要置于最底层,这样我们的纹理效果就出来了。

用动图演示下(为了方便展示,背景颜色的透明度调低了些):

2022-06-15 19.53.58.gif

模拟随机星空背景纹理

image.png

这个纹理也是比较典型的纹理,可以点进去看下整体效果:

image.png

很显然,这个背景中出现的星光是使用径向渐变生成的,但是如果生成随机效果呢?

也很简单,依旧是运用background属性的多背景特性,声明多个定位、背景大小均不同的背景进行叠加就可以产生随机效果。

我们先写一个星光试试:

background: radial-gradient(white, rgba(255,255,255,.2) 2px, transparent 40px); 
background-size: 350px 350px;
background-position: 0 0;
background-color: black;
复制代码

这样就生成一个单一的星光了:

image.png

然后我们再叠加两个不一样的星光,调整一下position和size值:

这样我们基本就完成了一个星空背景。如果想要更自然的背景还可以对参数多进行调整,或者增加几个背景。

总结

通过对上面的几个典型案例的分析,基本上大部分的纹理都可以用通用的处理方法来分析实现。

文中举例的纹理都是相对来说较为简单清晰的,如果大家已经掌握了方法,不妨去网站中继续挑战一下其他更有难度的纹理实现。

猜你喜欢

转载自juejin.im/post/7109703928068964388