svg背景,渐变,蒙层,剪切详解以及与css的对比

567 阅读4分钟

背景

svg背景可以对照css的background学习,但是css对背景的控制更加精细,其实叫做svg背景并不准确,叫做svg图案应该更好,svg背景使用pattern元素,相当于css的background-image。例如如下代码:

<svg width="500" height="500">
    <defs>
        <pattern id="topson" x="0" y="0" width="20%" height="20%" patternUnits="objectBoundingBox">
            <path d="M0 0 Q 20 5 10 10 T 20 20" style="stroke:rgb(34, 69, 223);fill:none"></path>
            <path d="M 0 0 h 20 v 20 h -20 z" style="stroke:rgb(247, 89, 89);fill:none;"></path>
        </pattern>
    </defs>
    <rect x="20" y="20" width="100" height="100" style="fill:url(#topson);stroke:black;"/>
    <rect x="135" y="20" width="70" height="80" style="fill:url(#topson);stroke:black;"/>
    <rect x="225" y="20" width="150" height="130" style="fill:url(#topson);stroke:black;"/>
</svg>

效果如下:
1663114393177.png
注意这里面的patternUnits属性,是用来控制渲染模式的,也就是说这个属性可以控制背景图案是以一定比例填充还是平铺。上面的例子是按照一定比例填充,由于宽度定义了20%,所以不论矩形的宽度多大,最多只能放置5个图案,如果想让图案平铺的话使用userSpaceOnUse属性值,并且不能再定义百分比。例如:

<svg width="500" height="500">
    <defs>
        <pattern id="topson" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
            <path d="M0 0 Q 20 5 10 10 T 20 20" style="stroke:rgb(34, 69, 223);fill:none"></path>
            <path d="M 0 0 h 20 v 20 h -20 z" style="stroke:rgb(247, 89, 89);fill:none;"></path>
        </pattern>
    </defs>
    <rect x="20" y="20" width="100" height="100" style="fill:url(#topson);stroke:black;"/>
    <rect x="135" y="20" width="70" height="80" style="fill:url(#topson);stroke:black;"/>
    <rect x="225" y="20" width="150" height="130" style="fill:url(#topson);stroke:black;"/>
</svg>

效果如下:
1663114947479.png
如果使用userSpaceOnUse的话在做图形位移动画的时候会有下面的效果:
pattern.gif
可以看到只改变矩形的位置,矩形里面的pattern并不会随着改变,所以我认为svg这点的设计并不好,如果想让背景平铺,并且不随元素位置发生移动,使用svg结合css使用能达到预期效果

divp.gif

虽然css的background-image很强大了,对于比较复杂的背景还是建议引用svg文件。理由有两个
1.svg的图案是可以嵌套的,可以做出很多复杂背景,虽然项目中不常用。
2.可以在svg中加入动画让背景动起来,要比加入gif动图小的多。
一个简单的例子:
divpb.gif

渐变

svg支持两种渐变:线性渐变和径向渐变,
css支持三种渐变:线性渐变和径向渐变和锥形渐变
可以看到css颜色渐变的种类是比svg齐全的,下面介绍svg渐变,顺带说一下css锥形渐变。

线性渐变

svg的线性渐变使用linearGradient元素,先看一个例子:

<svg width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" >
    <defs>
        <linearGradient id="line_gra">
            <stop offset="0%" stop-color="#ff0000"></stop>
            <stop offset="100%" stop-color="#00ff00"></stop>
        </linearGradient>
    </defs>
    <rect x="20" y="20" width="300" height="50" stroke="black" fill="url(#line_gra)"></rect>
</svg>

1663123568683.png
这是一个简单的双色渐变,除了渐变颜色还可以控制渐变透明度例如:

<svg width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" >
    <defs>
        <linearGradient id="line_gra">
            <stop offset="0%" stop-color="#ff0000" stop-opacity="1.0"></stop>
            <stop offset="50%" stop-color="#00ff00" stop-opacity="0.1"></stop>
            <stop offset="100%" stop-color="#0000ff" stop-opacity="1.0"></stop>
        </linearGradient>
    </defs>
    <rect x="20" y="20" width="300" height="50" stroke="black" fill="url(#line_gra)"></rect>
</svg>

1663124076086.png,
接下来就是控制渐变色的方向了,看下面的例子:

<svg width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" >
    <defs>
        <linearGradient id="line_gra">
            <stop offset="0%" stop-color="#ff0000" stop-opacity="1.0"></stop>
            <stop offset="50%" stop-color="#00ff00" stop-opacity="0.1"></stop>
            <stop offset="100%" stop-color="#0000ff" stop-opacity="1.0"></stop>
        </linearGradient>
        <linearGradient id="to_down" x1="0%" y1="0%" x2="100%" y2="100%" xlink:href="#line_gra"></linearGradient>
    </defs>
    <rect x="20" y="20" width="300" height="50" stroke="black" fill="url(#to_down)"></rect>
</svg>

image.png
我们可以控制渐变的其实位置,但是如果我们的渐变不是从0%到100%,而是从30%到60%,那么0%-30%的部分和60%-100%的部分如何渲染呢?看一下例子:

<svg width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" >
    <defs>
        <linearGradient id="line_gra" x1="30%" y1="50%" x2="60%" y2="50%">
            <stop offset="0%" stop-color="#ff0000"></stop>
            <stop offset="50%" stop-color="#00ff00"></stop>
            <stop offset="100%" stop-color="#0000ff"></stop>
        </linearGradient>
    </defs>
    <rect x="20" y="20" width="300" height="50" stroke="black" fill="url(#line_gra)"></rect>
</svg>

1663124698931.png
可以看到另外两部分是按照起始或者结束的值渲染的,这个渲染效果和css是一致的,但是svg的一个属性spreadMethod可以定义超出部分的渲染模式。有pad/repeat/reflect三种模式:
pad是默认模式,就是按照起点和终点扩展。
repeat:重复起点终点充满整个对象
reflect:渐变会按照起点终点排列来充满对象
概念有点不太好理解,不如直接上图,

1663125917175.png, css里对这方面的控制不如svg。css中有个repeating-linear-gradient,值用来控制重复效果同repeat,但是没有提供reflect模式。

径向渐变

径向渐变,表示的是圆形路径从圆心向外展开,svg中的语法和线性渐变基本相同,使用radialGradient元素,例如:

<svg width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" >
    <defs>
        <radialGradient id="radia_gra" cx="0%" cy="0%" r="80%">
            <stop offset="0%" stop-color="#ff0000"></stop>
            <stop offset="50%" stop-color="#00ff00"></stop>
            <stop offset="100%" stop-color="#0000ff"></stop>
        </radialGradient>
        <radialGradient id="repeated" xlink:href="#line_gra" spreadMethod="repeat"></radialGradient>
    </defs>
    <rect x="20" y="20" width="300" height="300" stroke="black" fill="url(#radia_gra)"></rect>
</svg>

1663204353687.png
cx,cy表示圆心位置,r表示渐变半径,关于半径说明一下,假设将半径设置为100%,渐变半径是矩形的边长,如果想让渐变充满整个矩形,可以将半径设置为2的平方根,就是1.41即141%。
我们还可以修改圆形的渐变圆心,通过fx,fy,修改例如:

<svg width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" >
    <defs>
        <radialGradient id="radia_gra" cx="0%" cy="0%" r="80%" fx="50%" fy="50%">
            <stop offset="0%" stop-color="#ff0000"></stop>
            <stop offset="50%" stop-color="#00ff00"></stop>
            <stop offset="100%" stop-color="#0000ff"></stop>
        </radialGradient>
        <radialGradient id="repeated" xlink:href="#line_gra" spreadMethod="repeat"></radialGradient>
    </defs>
    <rect x="20" y="20" width="300" height="300" stroke="black" fill="url(#radia_gra)"></rect>
</svg>

1663204836724.png
有兴趣的同学可以尝试一下r<fx/fy的情况,还是比较有意思的。可以看到像是一个光照的效果,径向渐变的渲染模式spreadMethod和线性渐变是一样的,有兴趣的同学可以自己尝试。不再重复。

css锥形渐变

css提供了另外一种渐变,锥形渐变,简单介绍一下,说明css和svg的区别,看如下代码:

  .coneGrid{
        width:300px;
        height:300px;
        background-image: conic-gradient(from 45deg at 40% 40%,rgb(209, 57, 57), rgb(56, 214, 16));
        border-radius: 50%;
    }
    

1663205930248.png这种渐变可以做像是雷达扫描的效果。

剪切

svg剪切要剪掉图像的一部分,svg使用clipth元素,css也支持clip-path,我在前面的文章有过介绍,path的语法是一样的。下面先看一个简单的svg的剪切例子:

<svg width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" >
    <defs>
        <clipPath id="circleClip">
            <circle cx="25" cy="75" r="200" stroke="red" fill="transparent" stroke-width="5"/>
        </clipPath>
        <radialGradient id="radia_gra" cx="0%" cy="0%" r="141%" fx="40%" fy="40%">
            <stop offset="0%" stop-color="#ff0000"></stop>
            <stop offset="50%" stop-color="#00ff00"></stop>
            <stop offset="100%" stop-color="#0000ff"></stop>
        </radialGradient>
    </defs>
    <rect x="20" y="20" width="300" height="300" stroke="black" fill="url(#radia_gra)" clip-path="url(#circleClip)"></rect>
</svg>

1663210726297.png
clipPath里面可以有多个路径,甚至可以是文字,

<svg width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" >
    <defs>
        <clipPath id="circleClip">
            <circle cx="25" cy="75" r="200" stroke="red" fill="transparent" stroke-width="5"/>
            <text x="180" y="200" style="font-family: 'Courier New', Courier, monospace;font-size: 48pt;fill:none;stroke:black;font-weight: bolder;">clip</text>
        </clipPath>
        <radialGradient id="radia_gra" cx="0%" cy="0%" r="141%" fx="40%" fy="40%">
            <stop offset="0%" stop-color="#ff0000"></stop>
            <stop offset="50%" stop-color="#00ff00"></stop>
            <stop offset="100%" stop-color="#0000ff"></stop>
        </radialGradient>
    </defs>
    <rect x="20" y="20" width="300" height="300" stroke="black" fill="url(#radia_gra)" clip-path="url(#circleClip)"></rect>
</svg>

1663211357915.png
这就很容易联想到css3的-webkit-background-clip:text属性,做出炫酷的文字效果。当然clipPath里面还可以使用path路径,不过路径是需要闭合的。

蒙层

svg的蒙层是一个不太好理解的特性,可以理解为给元素一个透明度,但是svg并没有直接取蒙层元素的透明度,而是使用下面的计算公式:

(0.2125red + 0.7154green + 0.0721*blue)*opacity

将蒙层的红绿蓝色值都使用上了。

下面我们详细探究一下,看如下例子:

<svg width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" >
    <defs>
        <mask id="remark" x="0" y="0" width="1" height="1" maskContentUnits="objectBoundingBox">
            <rect x="0" y="0" width="1" height="1" fill="#ff0000"></rect>
        </mask>
        <mask id="grmark" x="0" y="0" width="1" height="1" maskContentUnits="objectBoundingBox">
            <rect x="0" y="0" width="1" height="1" fill="#00ff00"></rect>
        </mask>
        <mask id="blmark" x="0" y="0" width="1" height="1" maskContentUnits="objectBoundingBox">
            <rect x="0" y="0" width="1" height="1" fill="#0000ff"></rect>
        </mask>
        <mask id="whitemark" x="0" y="0" width="1" height="1" maskContentUnits="objectBoundingBox">
            <rect x="0" y="0" width="1" height="1" fill="#ffffff"></rect>
        </mask>
    </defs>
    <g style="mask:url(#remark);font-size: 28px;text-anchor:middle">
        <circle cx="35" cy="115" r="30" fill="#ff0000"/>
        <text x="35" y="220">red</text>
    </g>
    <circle cx="35" cy="45" r="30" fill="#f00" fill-opacity="0.2125"/>
    <g style="mask:url(#grmark);font-size: 28px;text-anchor:middle">
        <circle cx="135" cy="115" r="30" fill="#ff0000"/>
        <text x="135" y="220">green</text>
    </g>
    <circle cx="135" cy="45" r="30" fill="#ff0000"/>
    <g style="mask:url(#blmark);font-size: 28px;text-anchor:middle">
        <circle cx="235" cy="115" r="30" fill="#ff0000"/>
        <text x="235" y="220">blue</text>
    </g>
    <circle cx="235" cy="45" r="30" fill="#ff0000"/>
    <g style="mask:url(#whitemark);font-size: 28px;text-anchor:middle">
        <circle cx="335" cy="115" r="30" fill="#ff0000"/>
        <text x="335" y="220">white</text>
    </g>
    <circle cx="335" cy="45" r="30" fill="#ff0000"/>
</svg>

效果如下:

1663290804335.png
在计算的过程中0-255会映射到0-1上进行计算。
如果只是简单的给元素加上透明度,是没必要使用蒙层的,关键是我们可以使用蒙层结合渐变色,做出同一个元素不同部分透明度不同的效果,这是比较有用的。下一节我们会使用蒙层,剪切,渐变做出一些不错的效果。