图案
patterns(图案)是SVG中用到的最让人混淆的填充类型之一。它的功能非常强大,所以我认为他们值得讨论一下并且我们应至少对他们有最基本的了解。跟渐变一样,<pattern>
需要放在SVG文档的<defs>
内部。
<?xml version="1.0" standalone="no"?>
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<linearGradient id="Gradient1">
<stop offset="5%" stop-color="white"/>
<stop offset="95%" stop-color="blue"/>
</linearGradient>
<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">
<stop offset="5%" stop-color="red"/>
<stop offset="95%" stop-color="orange"/>
</linearGradient>
<pattern id="Pattern" x="0" y="0" width=".25" height=".25">
<rect x="0" y="0" width="50" height="50" fill="skyblue"/>
<rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)"/>
<circle cx="25" cy="25" r="20" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
</defs>
<rect fill="url(#Pattern)" stroke="black" x="0" y="0" width="200" height="200"/>
</svg>
在pattern元素内部你可以包含任何之前包含过的其它基本形状,并且每个形状都可以使用之前学习过的任何样式样式化,包括渐变和半透明。这里我们在pattern中绘制两个矩形(两个矩形互相重叠,一个矩形是另一个矩形大小的二倍,且用于填充整个pattern)和一个圆。
关于pattern容易混淆的事是,pattern定义了一个单元系统以及他们的大小。上例中,我们在pattern元素上定义了width和height属性,用于描述在重复下一个图案之前应该跨过多远。如果你想要在绘制时偏移矩形的开始点,也可以使用x和y属性,原因如下。
就像前面使用了gradientUnits属性,同样的pattern也有一个属性patternUnits用于描述我们使用的属性单元。这同之前使用的objectBoundingBox默认值一样,所以当一个值为1时,它被缩放到应用pattern对象的宽高值。因此,我们希望pattern垂直和水平的重复4次,所以宽高被设置位0.25,这一位置pattern的宽高仅为总外框大小的0.25。
与渐变不同,pattern有第二个属性patternContentUnits,它描述了pattern元素基于基本形状使用的单元系统,这个属性默认值为userSpaceOnUse,与patternUnits属性相反,这意味着除非你至少指定其中一个属性值(patternContentUnits或patternUnits),否则在pattern中绘制的形状将与pattern元素使用的坐标系不同,当你手写这部分时会容易混淆。为了使上例生效,我们必须考虑我们的边框(200像素)大小和我们实际希望pattern垂直和水平重复4次的需求。这意味着每个pattern单元应该是50x50的方形,pattern中的两个矩形和圆形的大小会被缩放适应到一个50x50的边框里,任何我们绘制在边框外的内容都不会显示。因为我们希望pattern从边框的左上角里开始,所以pattern也必须偏移10像素,也就是pattern的x和y属性需要调整为 10/200=0.05。
如果对象改变了大小,pattern会自适应其大小,但是对象里面的内容不会自适应。所以当我们在pattern中还是放置4个重复的pattern时,组成pattern的对象将不会保持相同的大小,同时在他们之间会有大片空白区域。通过改变patternContentUnits属性,我们可以把所有的元素放到相同的单元系统中:
<pattern id="Pattern" width=".25" height=".25" patternContentUnits="objectBoundingBox">
<rect x="0" y="0" width=".25" height=".25" fill="skyblue"/>
<rect x="0" y="0" width=".125" height=".125" fill="url(#Gradient2)"/>
<circle cx=".125" cy=".125" r=".1" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
现在,因为pattern内容与pattern本身处于相同的单元系统中,所以我们不用偏移边框以使pattern在正确的位置上开始,并且即使对象变大,pattern也会自动的缩放以保证pattern内部的对象数目和重复不变。这与userSpaceOnUse系统不同,userSpaceOnUse系统中如果对象改变大小,pattern本身会保持不变,只是重复更多次去填满边框。
它有一点点的副作用,在Gecko中的圆如果半径设置得小于0.075(尽管半径应该设置的比这个值大得多。这个可能是pattern元素中的一个bug,或者也不算bug,我也不太清楚)的话绘制的时候可能会出现问题,为了规避这个问题,可能最好的办法是尽量避免在objectBoundingBox单元中绘制图形。
在你想要使用pattern的时候,可能你并不中意这些方法中的任何一个,Pattern通常都是有确认的大小并且重复他们自己,与对象形状独立开来。要想创建这种pattern,pattern和它的内容必须在当前用户空间中绘制,这样当对象在做如下操作时他们才不会改变形状:
<pattern id="Pattern" x="10" y="10" width="50" height="50" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="50" height="50" fill="skyblue"/>
<rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)"/>
<circle cx="25" cy="25" r="20" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
当然,这意味着如果你后续改变了对象大小,pattern也不会缩放。上述三个举例在下图中放在一个矩形中展示,高度被轻微拉伸到300px,但是我注意到这不是完整的图片,并且有些其他选项可能你的应用不支持。