裁剪以适合 svg 模式

Posted

技术标签:

【中文标题】裁剪以适合 svg 模式【英文标题】:Crop to fit an svg pattern 【发布时间】:2014-05-18 00:26:17 【问题描述】:

我有一些模式,每个模式中都有一个图像。我需要将图像缩放到其容器的全宽或全高,即路径,同时保持它们的比例。本质上,如果您设置min-width:100%; min-height:100%;,它们需要表现得像 html 图像一样。

我以前很少使用 svgs,也不知道要更改哪些属性才能获得这种行为。我一直在尝试viewBoxpreserveAspectRatiopatternUnits 等的各种组合,但我似乎无法得到我想要的。

【问题讨论】:

模式并不适合这种用例。最好使用过滤器。 @MichaelMullany 过滤器可能会降低浏览器的速度,但我在下面描述的大小和单位注意事项与您使用带有<feImage> 元素的过滤器大致相同。 【参考方案1】:

要使其工作,您需要了解 objectBoundingBox 单位在 SVG 中的工作原理,以及 preserveAspectRatio 的工作原理。

Object Bounding Box Units

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

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

所以第一步:要创建一个完全填充形状的图案,您需要:

将图案的高度和宽度声明为100%(或1);默认情况下,这些将相对于边界框进行解释)。 声明patternContentUnits="objectBoundingBox"。 调整内容(您的图片)的大小,使其高度和宽度均为 1。

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

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

Preserving Aspect Ratio

preserveAspectRatio 属性适用于图像和任何可以具有viewBox 属性的元素:父<svg>、嵌套<svg><symbol><marker><pattern>。对于图像,纵横比是根据图像的固有宽高比计算得出的,对于所有其他图像,纵横比是根据viewBox attribute 中的宽高比计算得出的。

对于任一类型的元素,如果您声明的元素的高度或宽度与纵横比不匹配,preserveAspectRatio 属性将确定内容是否将被拉伸以适应 (none),大小为适合一个维度并在另一个维度中裁剪 (slice) 或缩小以适应两个维度并留出额外空间 (meet);对于meetslice 选项,您还可以指定如何对齐空间中的内容。

但是,重要的是要注意可用空间的纵横比是在当前坐标系中计算的,而不是在屏幕像素中。因此,如果更高级别的 viewBox 或转换改变了纵横比,即使在当前元素上设置了 preserveAspectRatio 属性,事情仍然会发生扭曲。

要知道的另一件事是 default 值通常是 not none。对于<image><pattern> 元素,默认值为xMidYMid meet - 即缩小以适合和居中。当然,此默认设置仅在模式元素具有viewBox 属性时才对模式元素产生影响(否则,假定它没有要保留的纵横比)。

您想为preserveAspectRatio 使用什么值取决于图像和设计:

是否应该拉伸图像以适应形状preserveAspectRatio="none"? 是否应保持图像纵横比,但调整大小以完全适合或覆盖形状?

在第一种情况下(拉伸),你不需要对<pattern>元素做任何事情(没有viewBox意味着没有纵横比控制),但是你需要专门关闭图像的纵横比控制。

相比之下,如果您想避免图像失真,您需要:

<pattern> 元素上设置viewBox preserveAspectRatio 属性; 如果您想要不同于默认值的内容,请在 <image> 上设置 preserveAspectRatio 属性。

工作示例

This fiddle shows three ways of getting a pattern image to fill a shape.

顶行关闭了纵横比控制。

<!-- pattern1 - no aspect ratio control -->
<pattern id="pattern1"  
         patternContentUnits="objectBoundingBox">
    <image   preserveAspectRatio="none" 
           xlink:href="/*url*/" />
</pattern>

中间行对&lt;image&gt;元素进行了纵横比控制,因此图片被裁剪以适应图案,但在矩形中绘制图案时图片仍然失真,因为定义坐标的objectBoundingBox单位系统的高度与宽度是不同的。 (圆圈中的图像没有失真,因为圆圈的边界框是正方形。)

<!-- pattern2 - aspect ratio control on the image only -->
<pattern id="pattern2"  
         patternContentUnits="objectBoundingBox">
    <image   preserveAspectRatio="xMidYMid slice" 
           xlink:href="/*url*/" />
</pattern>

底行在图像图案上都设置了preserveAspectRatio(在图案上还设置了viewBox)。图像被裁剪但未被拉伸。

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

源图片by Stefan Krause, from Wikimedia Commons。原来的纵横比是 4:6 纵向模式。

* 2015-04-03 更正

【讨论】:

感谢您提供非常详细的回复!我认为我缺少的主要是如何使用preserveAspectRatio,但我从中学到了很多。 很高兴你明白了。我认为这是一个足够复杂的话题,值得进行一次从头到脚的讨论,这可以在未来帮助其他人,而不是花一打来回试图找出难题的哪一块绊倒了你起来。 阿米莉亚,这是一个绝妙的解释。您应该考虑向 webplatform.org 上的模式元素文档贡献一些版本 - 这非常需要内容 感谢@MichaelMullany。是的,我应该再看看 webplatform.org,看看有什么我可以帮忙的。我喜欢这个项目的想法,但我知道当我需要自己查找某些内容时,我仍然倾向于返回 MDN,因为它更有可能有内容。 @Alexandr_TT 链接已修复。如果以后遇到其他人,需要将http://fiddle.jshell.net替换为https://jsfiddle.net/

以上是关于裁剪以适合 svg 模式的主要内容,如果未能解决你的问题,请参考以下文章

我如何“裁剪” svg 以适应父母的形状?

libVLC 视频裁剪

python - svg 或 pdf 的破坏性裁剪和调整大小

UIImage 调整大小并裁剪以适合

图像裁剪以适合 GridView

当图像重新缩放以适合画布时,织物 js 上的裁剪功能无法正常工作