裁剪以适应SVG图案

25

我有一些图案,每个图案中都有一个单独的图片。我需要这些图片缩放到其路径所在容器的完整宽度或高度,同时保持其比例不变。基本上,他们需要像设置 min-width:100%; min-height:100%; 的HTML图像一样。

我以前没有使用过 SVG,并不知道要更改哪些属性才能实现这种行为。我尝试了各种组合,包括 viewBox, preserveAspectRatio, patternUnits 等等,但似乎都不能得到我想要的结果。


模式不太适合这种用例。最好使用过滤器。 - Michael Mullany
1
@MichaelMullany 过滤器会减慢浏览器的速度,但我在下面描述的大小和单位考虑与您使用<feImage>元素的过滤器大致相同。 - AmeliaBR
1个回答

82
To get this to work, you need to understand how objectBoundingBox units work in SVG, and also how preserveAspectRatio works.

对象边界框单位

渐变、图案和许多其他SVG功能的大小和内容可以通过指定objectBoundingBox作为单位来根据正在绘制的对象(path, rect, circle)的大小来指定。相反,总是使用userSpaceOnUse,它使用绘制形状的坐标系统。

通常,对象边界框单位是声明图形填充元素的大小位置的默认值;您可以通过在<pattern>元素上设置patternUnits属性来更改这个值。然而,用户空间单位通常是用于内容图形中使用的任何单位的默认值;要更改此值,您需要设置patternContentUnits属性。

首先,要创建一个完全填充形状的图案,您需要执行以下操作:
  • 将图案的高度和宽度声明为100%(或1);这些默认情况下将被解释为相对于边界框。
  • 声明patternContentUnits =“objectBoundingBox”
  • 调整内容(您的图像)的大小,使其具有1的高度和宽度。

您不能在图案内容本身(即图像尺寸)中使用100%作为1个对象边界框单位的同义词;百分比是相对于SVG大小而不是objectBoundingBox解释的。*

我应该提到,由于您说您的形状是<path>元素,因此对象边界框是与路径绘制的坐标系垂直且包含所有路径点的最小矩形。它不包括描边。例如,一条水平直线具有零高度的边界框;倾斜线具有边界框矩形,使得线是框的对角线。如果您的路径形状笨拙且/或与坐标系不太对齐,则边界框可能比路径大得多。

保持纵横比

preserveAspectRatio属性适用于图像和任何可以有viewBox属性的元素:父级<svg>、嵌套<svg><symbol><marker><pattern>。对于图像,它的纵横比是从图像固有的宽度:高度比率计算出来的,对于其他所有元素,则是从viewBox属性中的宽度:高度数字计算出来的。

无论是哪种元素,如果您为元素声明的高度或宽度与其纵横比不匹配,则preserveAspectRatio属性将确定内容是被拉伸适应(none),在一个维度上调整大小并在另一个维度上裁剪(slice),还是缩小以适应两个维度并留有额外空间(meet);对于meetslice选项,您还需要指定如何在空间中对齐内容。
然而,需要注意的是可用空间的纵横比是在当前坐标系中计算的,而不是在屏幕像素中计算的。因此,如果更高级别的viewBox或变换改变了纵横比,则即使在当前元素上设置了preserveAspectRatio属性,仍然可能会出现扭曲。
另外需要知道的是,默认值通常不是none。对于<image><pattern>元素,其默认值为xMidYMid meet——即缩小以适应并居中。当然,此默认值仅在图案元素具有viewBox属性时才会影响图案元素(否则,它被认为没有要保留的纵横比)。
你想使用什么样的preserveAspectRatio值将取决于图像和设计:
  • 图像是否应该被拉伸以适应形状preserveAspectRatio="none"
  • 图像纵横比是否应该保持,但大小应该完全适合或覆盖形状?
在第一种情况下(拉伸),您不需要对<pattern>元素进行任何操作(没有视口框意味着没有纵横比控制),但您确实需要明确关闭图像上的纵横比控制。
相反,如果您想避免图像失真,则需要:
请在<pattern>元素上设置viewBoxpreserveAspectRatio属性;如果您想要与默认值不同的内容,请在<image>上设置preserveAspectRatio属性。这个示例展示了三种使图案填充形状的方法。
  • The top row has aspect control turned off.

    <!-- pattern1 - no aspect ratio control -->
    <pattern id="pattern1" height="100%" width="100%"
             patternContentUnits="objectBoundingBox">
        <image height="1" width="1" preserveAspectRatio="none" 
               xlink:href="/*url*/" />
    </pattern>
    
  • The middle row has aspect ratio control on the <image> element so the picture is cropped to fit the pattern, but the picture is still distorted when the pattern is drawn in the rectangle because the objectBoundingBox units that define the coordinate system are different for height versus width. (The image in the circle isn't distorted because the circle's bounding box is a square.)

    <!-- pattern2 - aspect ratio control on the image only -->
    <pattern id="pattern2" height="100%" width="100%"
             patternContentUnits="objectBoundingBox">
        <image height="1" width="1" preserveAspectRatio="xMidYMid slice" 
               xlink:href="/*url*/" />
    </pattern>
    
  • The bottom row has preserveAspectRatio set on both the image and the pattern (and also a viewBox set on the pattern). The image gets cropped but not stretched.

    <!-- pattern3 - aspect ratio control on both image and pattern -->
    <pattern id="pattern3" height="100%" width="100%"
             patternContentUnits="objectBoundingBox" 
             viewBox="0 0 1 1" preserveAspectRatio="xMidYMid slice">
        <image height="1" width="1"  preserveAspectRatio="xMidYMid slice" 
               xlink:href="/*url*/" />
    </pattern>
    

Output from the JS fiddle example, showing the images described

Source image 由Stefan Krause从维基共享资源提供。原始宽高比为4:6纵向模式。
* 2015-04-03更正

3
非常感谢您详尽的回复!我想我之前没明白的主要是如何使用preserveAspectRatio,但是通过这个回复,我学到了很多东西。 - raphaeltm
2
很高兴你解决了它。我决定这是一个足够复杂的主题,值得进行全面讨论,以帮助未来的其他人,而不是花费十几个来回的评论来弄清楚哪个拼图的部分使你困惑。 - AmeliaBR
1
Amelia,这是一个非常棒的解释。你应该考虑向webplatform.org的模式元素文档贡献一些版本——它非常需要内容。 - Michael Mullany
1
感谢@MichaelMullany。是的,我应该再看看webplatform.org,看看是否有什么可以帮助的地方。我喜欢这个项目的想法,但当我需要自己查找某些内容时,我仍然倾向于返回MDN,因为它更有可能拥有相关内容。 - AmeliaBR
2
@Alexandr_TT 链接已修复。如果您在将来遇到其他链接,请用 https://jsfiddle.net/ 替换 http://fiddle.jshell.net - AmeliaBR
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接