第 8 章 图案和渐变
图案
要使用图案,首先要定义一个水平或者垂直方向重复的图形对象,然后用它填充另一个对象或作为画笔使用。这个图形对象称为 tile (瓷砖),因为使用图案填充对象的行为很像在地面上铺瓷砖的过程
本节使用如下曲线作为图案
<!-- @format -->
<svg>
<path d="M 0 0 Q 5 20 10 10 T 20 20" style="stroke: black; fill: none" />
<path d="M 0 0 h20 v20 h-20 z" style="stroke: gray; fill: none" />
</svg>
patternUnits
要创建一个图案,必须使用
<pattern>元素包裹描述图案的<path>元素,然后设置相应属性
用于设置如何排列图案
- 设置其值为
objectBoundingBox(默认值),指定图案左上角坐标,以及宽高(百分比或者 0-1 之间的小数)。结果:当被填充对象(此处为矩形)的尺寸不足时,会发生裁剪,当尺寸过大时,会由空隙 - 设置其值为
userSpaceOnUse,指定图案左上角坐标,以及宽高(按用户单位)。结果:图案之间会紧密贴合,裁剪只发生在被填充对象(此处为矩形)的边缘
objectBoundingBox 效果
userSpaceOnUse 效果
patternContentUnits
确定用什么单位表达图案数据本身
userSpaceOnUse:默认值objectBoundingBox:路径本身的数据点会被基于被填充的对象来确定
objectBoundingBox 效果
注意下面源码中的小数
<pattern
id="tile"
patternUnits="objectBoundingBox"
patternContentUnits="objectBoundingBox"
x="0"
y="0"
width=".2"
height=".2"
>
<path
d="M 0 0 Q .05 .20 .10 .10 T .20 .20"
style="stroke: black; fill: none; stroke-width: 0.01"
/>
<path
d="M 0 0 h 0.2 v 0.2 h-0.2z"
style="stroke: black; fill: none; stroke-width: 0.01"
/>
</pattern>
- 如果使用
userSpaceOnUse,则图案的边界框左上角应该在原点位置- 如果使用
objectBoundingBox,则需要在图案中减小stroke-width的值。图案的宽度也会以被填充对象的边界框作为参考,而不会使用用户单位,因此stroke-width为 1 会覆盖整个图案。在本例中,笔画宽度被设置为 0.01,也就是被填充对象边界框宽度和高度均值的 1%
结论:
patternUnits和patternContentUnits都使用userSpaceOnUse是最方便且符合常规认知的做法
如果想缩小现有的图形对象当作图案
- 使用
viewBox属性来缩放更容易。指定viewBox会覆盖任何patternContentUnits信息 - 设置
preserveAspectRatio属性
图案嵌套
图案也可以嵌套,和标记嵌套完全不同(很少需要嵌套标记),如果不使用图案嵌套,某些效果很难实现
渐变
可以使用渐变填充对象,可以是线性也可以是径向
linearGradient 元素
在特定的位置指定想要的颜色,被称为渐变点(gradient stop)。渐变点是渐变结构的一部分,颜色是表现的一部分
<!-- @format -->
<svg>
<defs>
<linearGradient id="two_hues">
<stop offset="0%" style="stop-color: #ffcc00" />
<stop offset="100%" style="stop-color: #0099cc" />
</linearGradient>
</defs>
<rect
x="20"
y="20"
width="200"
height="100"
style="fill: url(#two_hues); stroke: black"
/>
</svg>
<stop> 元素
offset:必须。用于确定线上哪个点的颜色应该等于stop-color。使用百分数或 0-1 之间的小数表示。0% 和 100% 位置设置渐变点不是必须的,但是通常都会设置stop-color:必须。被指定在style中,但是也可以指定为独立属性stop-opacity:可选。用于指定颜色的不透明度
定义渐变方向
默认是沿着水平线从左往右,若想改变方法,可设置渐变的起终点坐标,默认情况下也是使用百分数或小数指定
x1:起点x坐标y1:起点y坐标x2:终点x坐标y2:终点y坐标
<!-- @format -->
<svg width="100%" height="200">
<defs>
<linearGradient id="three_stops">
<stop offset="0%" style="stop-color: #ffcc00" />
<stop offset="33.3%" style="stop-color: #cc6699" />
<stop offset="100%" style="stop-color: #66cc99" />
</linearGradient>
<linearGradient
id="right_to_left"
xlink:href="#three_stops"
x1="100%"
y1="0%"
x2="0%"
y2="0%"
/>
<linearGradient
id="down"
xlink:href="#three_stops"
x1="0%"
y1="0%"
x2="0%"
y2="100%"
/>
<linearGradient
id="up"
xlink:href="#three_stops"
x1="0%"
y1="100%"
x2="0%"
y2="0%"
/>
<linearGradient
id="diagonal"
xlink:href="#three_stops"
x1="0%"
y1="0%"
x2="100%"
y2="100%"
/>
</defs>
<rect
x="40"
y="20"
width="200"
height="40"
style="fill: url(#three_stops); stroke: black"
/>
<rect
x="40"
y="70"
width="200"
height="40"
style="fill: url(#right_to_left); stroke: black"
/>
<rect
x="250"
y="20"
width="40"
height="200"
style="fill: url(#down); stroke: black"
/>
<rect
x="300"
y="20"
width="40"
height="200"
style="fill: url(#up); stroke: black"
/>
<rect
x="40"
y="120"
width="200"
height="100"
style="fill: url(#diagonal); stroke: black"
/>
</svg>
如果想要使用用户坐标空间而不是百分比指定渐变方向,设置
gradientUnits为userSpaceOnUse,而不是默认值objectBoundingBox即可
spreadMethod 属性
指定过渡方向不一定要从一角到另一角,如果指定从 (20%, 30%) 到 (40%, 80%),对于指定范围之外的颜色,则可以通过设置 spreadMethod 属性的值来选择不同的效果
pad:起始和结束渐变点会扩展到对象的边缘repeat:渐变会重复起点到终点的过程,直到填充满整个对象reflect:渐变会按终点到起点、起点到终点的排列重复,直到填充满整个对象
<!-- @format -->
<svg width="100%">
<defs>
<linearGradient id="partial" x1="20%" y1="30%" x2="40%" y2="80%">
<stop offset="0%" style="stop-color: #ffcc00" />
<stop offset="33.3%" style="stop-color: #cc6699" />
<stop offset="100%" style="stop-color: #66cc99" />
</linearGradient>
<linearGradient id="padded" xlink:href="#partial" spreadMethod="pad" />
<linearGradient
id="repeated"
xlink:href="#partial"
spreadMethod="repeat"
/>
<linearGradient
id="reflected"
xlink:href="#partial"
spreadMethod="reflect"
/>
<line
id="show-line"
x1="20"
y1="30"
x2="40"
y2="80"
style="stroke: white"
/>
</defs>
<rect
x="20"
y="20"
width="100"
height="100"
style="fill: url(#padded); stroke: black"
/>
<use xlink:href="#show-line" transform="translate(20, 20)" />
<rect
x="130"
y="20"
width="100"
height="100"
style="fill: url(#repeated); stroke: black"
/>
<use xlink:href="#show-line" transform="translate(130, 20)" />
<rect
x="240"
y="20"
width="100"
height="100"
style="fill: url(#reflected); stroke: black"
/>
<use xlink:href="#show-line" transform="translate(240, 20)" />
</svg>
radialGradient 元素
每一个渐变点表示一个圆形路径,从中心点向外扩散,如果填充对象的边界框不是正方形,过渡路径会变成椭圆来匹配边界框的长宽比,它的设置方式和线性渐变大致相同
<!-- @format -->
<svg width="100%">
<defs>
<radialGradient id="three_stops">
<stop offset="0%" style="stop-color: #f96" />
<stop offset="50%" style="stop-color: #9c9" />
<stop offset="100%" style="stop-color: #906" />
</radialGradient>
</defs>
<rect
x="20"
y="20"
width="100"
height="100"
style="fill: url(#three_stops); stroke: black"
/>
</svg>
定义径向渐变的范围
cx:中心点x坐标。默认值为 50%(对象边界框的水平中心点)cy:中心点y坐标。默认值为 50%(对象边界框的垂直中心点)r:半径。。默认值为 50%(对象边界框宽度/高度的一半)
所有这些属性值都是对象外边框的百分比,默认值都为 50%
<!-- @format -->
<svg width="100%">
<defs>
<radialGradient id="center_origin" cx="0%" cy="0%" r="141%">
<stop offset="0%" style="stop-color: #f96" />
<stop offset="50%" style="stop-color: #9c9" />
<stop offset="100%" style="stop-color: #906" />
</radialGradient>
</defs>
<rect
x="20"
y="20"
width="100"
height="100"
style="fill: url(#center_origin); stroke: black"
/>
</svg>
上面例子中,
radialGradient的r被设置为 141%,而不是 100%,是因为用来测量半径的单位是对象边界框宽度和高度的平均值,而不是框的对角线,正方形对角线与边长的比例是 2 的平方根,即 1.41
fx、fy:焦点坐标。默认值和cx、cy一样,0% 点所在位置,也被称为焦点,默认为 100% 处渐变点所在圆的圆心
<!-- @format -->
<svg width="100%">
<defs>
<radialGradient
id="focal_set"
cx="0%"
cy="0%"
fx="50%"
fy="50%"
r="100%"
>
<stop offset="0%" style="stop-color: #f96" />
<stop offset="50%" style="stop-color: #9c9" />
<stop offset="100%" style="stop-color: #906" />
</radialGradient>
</defs>
<rect
x="20"
y="20"
width="100"
height="100"
style="fill: url(#focal_set); stroke: black"
/>
</svg>
如果想用用户空间坐标而不是百分比确定圆的范围,要设置
gradientUnits为userSpaceOnUse,而不是默认值objectBoundingBox
径向渐变的 spreadMethod 属性
与线性渐变的一样
变换图案和渐变
有时候可能需要斜切、拉伸、旋转图案或者渐变,此时无需变换填充对象,而是变换用来填充对象的图案或者渐变。可以使用 gradientTransform 和 patternTransform 属性来实现
<!-- @format -->
<svg width="100%">
<defs>
<pattern
id="tile"
x="0"
y="0"
width="20%"
height="20%"
patternUnits="objectBoundingBox"
>
<path
d="M 0 0 Q 5 20 10 10 T 20 20"
style="stroke: black; fill: none"
/>
<path
d="M 0 0 h 20 v 20 h -20 z"
style="stroke: gray; fill: none"
/>
</pattern>
<pattern
id="skewed-tile"
patternTransform="skewY(15)"
xlink:href="#tile"
/>
<linearGradient id="plain">
<stop offset="0%" style="stop-color: #ffcc00" />
<stop offset="33.3%" style="stop-color: #cc6699" />
<stop offset="100%" style="stop-color: #66cc99" />
</linearGradient>
<linearGradient
id="skewed-gradient"
gradientTransform="skewX(10)"
xlink:href="#plain"
/>
</defs>
<rect
x="20"
y="10"
width="100"
height="100"
style="fill: url(#tile); stroke: black"
/>
<rect
x="135"
y="10"
width="100"
height="100"
style="fill: url(#skewed-tile); stroke: black"
/>
<rect
x="20"
y="120"
width="200"
height="50"
style="fill: url(#plain); stroke: black"
/>
<rect
x="20"
y="190"
width="200"
height="50"
style="fill: url(#skewed-gradient); stroke: black"
/>
</svg>
渐变和图案也可以应用于 stroke,这样子就生成了彩色或图案描边,此时通常会设置 stroke-width 为大于 1 的数字
在添加
stroke的时候,大小是基于objectBoundingBox来计算的。因为水平线和垂直线的边界框的宽度或高度默认为0,所以当图案和渐变作用于这些线条的时候,使用objectBoundingBox单位的渐变或者图案会被忽略,意味着它们不会被绘制出来,除非在style中指定备选笔画值,如style="stroke: url(#rainbow) red;"
如果图案或渐变定义在独立的文件中,设置备用填充和画笔是好习惯,以防文件不能加载或者
SVG阅读器不支持外部引用