6

这章开始的内容,以前从来没听说过,学起来有点苦逼T_T,只能放慢节奏一点点来了。断断续续写了好几天。这篇有点小长。

svg有两种填充方式:pattern和gradient。
pattern可以实现以指定的图案来填充一块区域。
gradient可以实现用渐变色来填充一块区域,渐变可以是线性(linearGradient)的,也可以是放射性(radialGradient)的。
这块区域可以是fill,也可以是stroke。

pattern

用<pattern>定义的图案,可以被用在其他元素的fill或者stroke属性中。

<pattern id="pa">
    //描绘pattern图案的svg元素
</pattern>
<rect x="0" y="0" height="100" width="100" style="fill:url(#pa); stroke:url(#pa)">

patternUnits

pattern有两种填充方式:objectBoundingBox和userSpaceOnUse

objectBoundingBox

一种可以理解为在指定区域内,规定沿x轴和y轴平铺指定数量的图案。这种方式对应的属性为patternUnits="objectBoundingBox"。objectBoundingBox也是patternUnits的默认属性值。

当patternUnits="objectBoundingBox"时,我们通过指定width和height来间接规定图案平铺的数量。因为这时,width和height被限制在0~1,或者0%~100%之间,即宽度或高度占填充区域高度或宽度的百分比。可想而知20%放5个,40%放2.5个。

<defs>
    <pattern id="tile" width="0.2" height="0.2" patternUnits="objectBoundingBox">
        <path d="M 0 0 Q 5 20 10 10 T 20 20" stroke="black" fill="none"></path>
        <rect x="0" y="0" width="20" height="20" stroke="grey" fill="none"></rect>
    </pattern>
</defs>
<rect x="20" y="20" width="100" height="100" fill="url(#tile)" stroke="grey"></rect>
<rect x="140" y="20" width="70" height="80" fill="url(#tile)" stroke="grey"></rect>
<rect x="230" y="20" width="150" height="180" fill="url(#tile)" stroke="grey"></rect>

clipboard.png

patternUnits并不会对内部的图案做缩放,所以第二张图中当宽度70的20%不足画出20*20的贝塞尔曲线时,曲线超出部分就被裁掉了。而第三张图中当宽度150的20%超过20时,曲线也不会被放大,超出部分由空白来填充。

默认情况下,pattern图案会将填充区域的左上角作为起点坐标(origin point)(0,0)。当然也可以通过设置x和y属性的值改变这个起点坐标。x和y分别表示相对于起点坐标的偏移量。在patternUnits="objectBoundingBox"情况下,x和y的值乘以填充区域相应的宽度和高度,即为实际起点坐标偏移量。

<defs>
    <pattern id="tile" x="0.1" y="0.1" width="0.2" height="0.2" patternUnits="objectBoundingBox">
        <path d="M 0 0 Q 5 20 10 10 T 20 20" stroke="black" fill="none"></path>
        <rect x="0" y="0" width="20" height="20" stroke="grey" fill="none"></rect>
    </pattern>
</defs>
<rect x="20" y="20" width="100" height="100" fill="url(#tile)" stroke="grey"></rect>
<rect x="140" y="20" width="70" height="80" fill="url(#tile)" stroke="grey"></rect>
<rect x="230" y="20" width="150" height="180" fill="url(#tile)" stroke="grey"></rect>

clipboard.png

userSpaceOnUse

另一种是将pattern的宽度和高度固定住,在指定区域内平铺,能铺多少铺多少,超出部分裁掉。对应的属性为patternUnits="userSpaceOnUse"。相应的width和height即pattern的宽度和高度。

<defs>
    <pattern id="tile2" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
        <path d="M 0 0 Q 5 20 10 10 T 20 20" stroke="grey" fill="none"></path>
        <rect x="0" y="0" width="20" height="20" stroke="grey" fill="none"></rect>
    </pattern>
</defs>
<rect x="0" y="0" width="100" height="100" fill="url(#tile2)" stroke="black"></rect>
<rect x="100" y="0" width="70" height="80" fill="url(#tile2)" stroke="black"></rect>
<rect x="170" y="0" width="150" height="130" fill="url(#tile2)" stroke="black"></rect>

clipboard.png

需要注意的是,userSpaceOnUse的pattern并不会在每个指定区域内重新以区域左上角开始排序。userSpaceOnUse的pattern的起点坐标只有一个,就是其x和y表示的在svg画布坐标系中的位置。
所以可以看到第二和第三个图相邻的两个图,贝塞尔曲线是连续的。我们可以改变pattern的x和y看下结果。

<defs>
    <pattern id="tile2" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
        <path d="M 0 0 Q 5 20 10 10 T 20 20" stroke="grey" fill="none"></path>
        <rect x="0" y="0" width="20" height="20" stroke="grey" fill="none"></rect>
    </pattern>
</defs>
<rect x="10" y="10" width="100" height="100" fill="url(#tile2)" stroke="black"></rect>
<rect x="110" y="20" width="70" height="80" fill="url(#tile2)" stroke="black"></rect>
<rect x="180" y="30" width="150" height="130" fill="url(#tile2)" stroke="black"></rect>

clipboard.png

patternContentUnits

pattern的缩放和排布由patternUnits控制,而pattern内部的图案的缩放和排布由patternContentUnits控制。
patternContentUnits也有两个属性:objectBoundingBox和userSpaceOnUse(默认属性值)

userSpaceOnUse

在userSpaceOnUse模式下,pattern内部元素的大小不会因为pattern的缩放而改变。userSpaceOnUse是patternContentUnits的默认属性值。

objectBoundingBox

在objectBoundingBox模式下,pattern内部元素所有属性的数值都会根据设置的比例,乘以pattern的width或height计算出实际长度。pattern内部元素所有属性的数值如果后面不带百分号%,都乘上100作为百分比数。所以像stroke-width默认值是1的这种属性,如果不指定一个数值,就会被当成100%来计算,结果就是撑满整个pattern。

<defs>
    <pattern id="tile1" patternUnits="objectBoundingBox"  x="0" y="0" width=".2" height=".2" patternContentUnits="objectBoundingBox">
        <path d="M 0 0 Q .05 .2 .1 .1 T .2 .2" style="stroke: black; fill: none; stroke-width: .01;"></path>
        <path d="M 0 0 h .2 v .2 h-.2z" style="stroke: black; fill: none; stroke-width: .01;"></path>
    </pattern>
</defgs>
<g transform="translate(20,20)">
    <rect x="0" y="0" width="100" height="100" style="fill: url(#tile1  ); stroke: black;"></rect>
</g>
<g transform="translate(135,20)">
    <rect x="0" y="0" width="70" height="80" style="fill: url(#tile1); stroke: black;"></rect>
</g>
<g transform="translate(220,20)">
    <rect x="0" y="0" width="150" height="80" style="fill: url(#tile1); stroke: black;"></rect>
</g>

clipboard.png

viewBox

可以在pattern的属性中使用viewBox完全替代patternContentUnits="objectBoundingBox",上例用viewBox的实现如下:

<pattern id="tile2" patternUnits="objectBoundingBox"  x="0" y="0" width=".2" height=".2" viewBox="0 0 20 20" preserveAspectRatio="none">
    <path d="M 0 0 Q 5 20 10 10 T 20 20" style="stroke: black; fill: none; stroke-width: 1;"></path>
    <path d="M 0 0 h 20 v 20 h-20z" style="stroke: black; fill: none; stroke-width: 1;"></path>
</pattern>

当然,通过设置不同的preserveAspectRatio还可以实现一些patternContentUnits="objectBoundingBox"无法实现的功能。如果有viewBox属性,patternContentUnits属性将被忽略。

pattern中内嵌pattern

允许在一个pattern中的元素内,嵌入另一个pattern。

<defs>
    <pattern id="stripe" x="0" y="0" width=".2" height=".3333" patternContentUnits="objectBoundingBox">
        <path d="M0 0h1" stroke="grey" stroke-width=".02"></path>
        <path d="M0 0v1" stroke="grey" stroke-width=".02"></path>
    </pattern>
    <pattern id="circle" x="0" y="0" width=".2" height=".25" viewBox="0 0 40 40">
        <circle cx="20" cy="20" r="15" fill="url(#stripe)" stroke="black"></circle>
    </pattern>
</defs>
<path d="M0 0 h300v300h-300z" fill="url(#circle)" stroke="black"></path>

clipboard.png
接着上篇继续看gradient

gradient

再来看看渐变。
渐变分两种,一种叫线性渐变(linearGradient),一种叫放射渐变(radialGradient)。

linearGradient

线性渐变的相关内容被定义在<linearGradient>元素内。用<stop>元素来定义渐变的关键点。可以设置多个<stop>元素。<stop>元素提供了几个属性让我们可以对渐变进行控制。

  • offset

  • stop-color

  • stop-opacity

  • gradientUnits

  • x1,y1,x2,y2

offset属性值范围为0%~100%,或者0~1.0。0表示渐变线上的起始位置,1为终点位置。
stop-color用来设置关键点处的颜色。
stop-opacity用来设置关键点处的透明度。
gradientUnits和前面的patternUnits类似,也是objectBoundingBox和userSpaceOnUse两个参数。默认值是objectBoundingBox。
x1,y1用来设置渐变线的起始坐标,x2,y2设置终点坐标。当gradientUnits为objectBoundingBox时,坐标值范围为0%~100%或0~1.0,即根据填充区域的宽高的百分比计算得出实际渐变线的起点和终点坐标。当gradientUnits为userSpaceOnUse时,坐标值表示svg画布坐标系中的位置。

<defs>
    <linearGradient id="linear" x1="1" y1="0" x2="0" y2="1">
        <stop offset="0" stop-color="black"></stop>
        <stop offset="1" stop-color="black" stop-opacity="0"></stop>
    </linearGradient>
    <linearGradient id="linear2" x1="210" y1="0" x2="100" y2="100" gradientUnits="userSpaceOnUse">
        <stop offset="0" stop-color="black"></stop>
        <stop offset="1" stop-color="white"></stop>
    </linearGradient>
</defs>
<path d="M0 0v100h100v-100z" fill="url(#linear)" stroke="black"></path>
<path d="M110 0v100h100v-100z" fill="url(#linear2)" stroke="black"></path>

clipboard.png

radialGradient

同linearGradient一样,也需要<stop>元素设置渐变关键点。<stop>元素的属性和linearGradient大致相同。

  • offset

  • stop-color

  • stop-opacity

  • gradientUnits

  • cx, cy

  • fx, fy

  • r

默认的,以圆心为起点(offset="0")放射性向外渐变,圆的边缘处为终点(offset="100%")。stop-color,stop-opacity,gradientUnits和linearGradient属性作用相同。(gradientUnits="userSpaceOnUse"个人感觉用的地方比较少,后面的例子都默认为objectBoundingBox的情况)
(cx,cy)用来设置渐变的圆心位置,更准确的说,是offset="100%"表示的最外层圆的圆心。以百分比表示。
(fx,fy)用来设置渐变的起点位置,就是offset="0"表示的圆心的坐标。以百分比表示。
r用来设置放射半径,以百分比表示。更准确的说,是根据填充区域高度的一半乘以r得出的长度作为y轴半径,根据宽度的一半乘以r得出的长度作为x轴半径。也就是说如果填充区域是个长方形,默认r="50%"时,渐变最外侧边缘的形状是个椭圆。
默认情况下,cx,cy,fx,fy,r都是50%,即渐变边缘的外圆圆心坐标和渐变起点坐标都是中心点,放射半径(rx, ry)为填充区域宽度和高度的1/2。

    <defs>
        <radialGradient id="radial">
            <stop offset="0" stop-color="grey"></stop>
            <stop offset="1" stop-color="grey" stop-opacity="0"></stop>
        </radialGradient>
        <radialGradient id="radial2" cx="0" cy="0" r="1.2">
            <stop offset="0" stop-color="grey"></stop>
            <stop offset="1" stop-color="grey" stop-opacity="0"></stop>
        </radialGradient>
        <radialGradient id="radial3" cx=".5" cy=".5" fx=".8" fy=".8">
            <stop offset="0.1" stop-color="grey" stop-opacity="0"></stop>
            <stop offset="1" stop-color="grey"></stop>
        </radialGradient>
    </defs>
    <circle r="75" cx="100" cy="100" fill="url(#radial)"></circle>
    <rect x="200" y="25" width="150" height="150" fill="url(#radial2)"></rect>
    <circle r="75" cx="450" cy="100" fill="url(#radial3)"></circle>

clipboard.png

spreadMethod

如果渐变线的起点不从0开始,或者终点不到1结束。比如:

<defs>
    <linearGradient id="linear" x1=".3" y1=".3" x2=".7" y2=".7">
        <stop offset="0" stop-color="#00F"></stop>
        <stop offset="1" stop-color="#FF0"></stop>
    </linearGradient>
</defs>
<rect x="10" y="10" height="100" width="100" fill="url(#linear)" stroke="none"></rect>

clipboard.png

svg默认自动将超出渐变线之外的部分用相应<stop>的stop-color填充。
可以使用spreadMethod改变超出部分颜色的填充方式。spreadMethod有三个属性值:

  • pad

  • repeat

  • reflect

pad为spreadMethod默认属性值。
repeat是以重复的方式填充超出部分。
reflect是以镜像的方式填充超出部分。

<defs>
    <linearGradient id="linear2" x1="0.3" y1="0.3" x2=".7" y2=".7" spreadMethod="repeat">
        <stop offset="0" stop-color="#00F"></stop>
        <stop offset="1" stop-color="#FF0"></stop>
    </linearGradient>
    <linearGradient id="linear3" x1="0.3" y1="0.3" x2=".7" y2=".7" spreadMethod="reflect">
        <stop offset="0" stop-color="#00F"></stop>
        <stop offset="1" stop-color="#FF0"></stop>
    </linearGradient>
</defs>
<rect x="120" y="10" height="100" width="100" fill="url(#linear2)" stroke="none"></rect>
<rect x="230" y="10" height="100" width="100" fill="url(#linear3)" stroke="none"></rect>

clipboard.png

Pattern和Gradient的形变

可以通过设置<pattern>和<gradient>元素的transform属性,对填充图案做变形。
<pattern>元素的形变属性为patternTransform, <linearGradient>和<radialGradient>元素的形变属性为gradientTransform。transform的属性值和SVG之transform中介绍的属性值一模一样。就简单写个demo看下就行了。

<defs>
    <radialGradient id="radial" gradientTransform="translate(.2,.2)">
        <stop offset="0" stop-color="grey"></stop>
        <stop offset="1" stop-color="grey" stop-opacity="0"></stop>
    </radialGradient>
    <radialGradient id="radial2" gradientTransform="translate(.375,.375) scale(.25) ">
        <stop offset="0" stop-color="grey"></stop>
        <stop offset="1" stop-color="grey" stop-opacity="0"></stop>
    </radialGradient>
    <radialGradient id="radial3" gradientTransform="translate(0,.25) scale(.5) skewX(45)">
        <stop offset="0" stop-color="grey"></stop>
        <stop offset="1" stop-color="grey" stop-opacity="0"></stop>
    </radialGradient>
</defs>
<rect x="0" y="0" height="100" width="100" fill="url(#radial)" stroke="grey"></rect>
<rect x="110" y="0" height="100" width="100" fill="url(#radial2)" stroke="grey"></rect>
<rect x="220" y="0" height="100" width="100" fill="url(#radial3)" stroke="grey"></rect>

clipboard.png


梦梦她爹
1.8k 声望122 粉丝

« 上一篇
SVG之Paths
下一篇 »
SVG之text