理解SVG坐标系统和变换: 建立新视窗在SVG绘制的任何一个时刻,你可以通过嵌套
<svg> 或者使用例如
<symbol> 的元素来建立新的viewport和用户坐标系。 在这篇文章中,我们将看一下我们如何这样做,以及这样做如何帮助我们控制SVG元素并让它们变得更加灵活(或流动)。 这是SVG坐标系和变换系列的第三篇也是最后一篇文章。 在第一篇中,包括了任何要理解SVG坐标系统基础的需要知道的内容;更具体的是,
SVG viewport, viewBox 和 preserveAspectRatio 属性。 在第二篇文章里,你可以了解到任何你需要了解的关于SVG系统变换的内容。 通过这篇文章,我假定你已经读了这个系列的第一部分关于SVG
viewport, viewBox 和 preserveAspectRatio 属性的内容。 在阅读这篇文章之前你不需要读第二篇关于坐标系变换的内容。 嵌套
<svg> 元素在第一部分我们讨论了
<svg> 元素如何为SVG画布内容建立一个视窗。 在SVG绘制过程中的任何一个时刻,你可以创建一个新的视窗其中包含的图形是通过把一个
<svg> 元素包含在另一个中绘制的。 通过建立新视窗,你隐性得建立了一个新视窗坐标系和新用户坐标系。 例如,试想有一个
<svg> 以及里面的内容:
<svg xmlns=”http://www.w3.org/2000/svg” xmlns:xlink=”http://www.w3.org/1999/xlink”>
<!– some SVG content –>
<svg>
<!– some inner SVG content –>
</svg>
<svg> 1 2 3 4 5 6
<svg xmlns=”http://www.w3.org/2000/svg” xmlns : xlink=”http://www.w3.org/1999/xlink”>
<!– some SVG content –>
<svg>
<!– some inner SVG content –>
</svg>
<svg>第一件需要注意的是内容
<svg> 元素不需要声明一个命名空间xmlns因为默认和外层
<svg> 的命名空间相同。当然,如果在HTML5文档中外层
<svg> 也不需要命名空间。 你可以使用一个嵌套的SVG来把元素组合在一起然后在父SVG中定位它们。 现在,你也可以把元素组合在一起并且使用组
<g> 来定位-通过把元素包括在一组
<g> 元素中。 你可以使用 transform 属性在画布中定位它们。 然而,使用
<svg> 肯定好过使用
<g> 。 使用x和y坐标来定位,在许多情况下,比使用变换更加方便。 另外,
<svg> 元素接受宽高值,
<g> 不行。 这意味着,
<svg> 也许并必要的,因为它可以创建一个新的viewport和坐标系,你可以不需要也不想要。
通过给
<svg> 声明宽高值,你把内容限制在通过 x , y , width 和 height
属性定义的viewport的边界。 任何超过边界的内容会被裁切。
如果你不声明 x 和 y 属性,它们默认是0。 如果你不声明
height 和 width 属性,
<svg> 会是父SVG宽度和高度的100%。 另外,声明用户坐标系而不是默认的也会影响内部
<svg> 的内容。 给
<svg> 内的元素百分比值的声明会根据
<svg>
计算,而不是外层
<svg> 。 例如,下面的代码会导致内层SVG等于
400 单位,里面的长方形是
200 个单位:
<svg
width=”800″
height=”600″>
<svg width=”50%”
..>
<rect
width=”50%”
…/></svg>
</svg>
12345
<svg width=”800″height=”600″>
<svg width=”50%”>
<rectwidth=”50%”…/> </svg>
</svg>如果最外层
<svg> 的宽度为100%(例如,如果它在一个文档中内联或者你想要它可以流动),内层SVG会扩展拉伸来保持宽度为外层SVG的一半-这是强制的。
嵌套SVG在给SVG画布中的元素增加灵活性和扩展性时尤其有用。
我们知道,使用 viewBox
值和 preserveAspectRatio
,我们已经可以创建响应式SVG。
最外层
<svg> 的宽度可以设置成100%来确保它扩展拉伸到它的容器(或页面)扩展或拉伸。
然后通过使用viewBox值和
preserveAspectRatio,我们可以保证SVG画布可以自适应viewport中的改变(最外层svg)。
我在CSSConf演讲的幻灯片中写到了关于响应式SVG的内容。
你可以在这里查看这个技术。
然而,当我们像这样创建一个响应式SVG,整个画布以及所有绘制在上面的元素都会有反应并且同时改变。
但有时候,你只想让图形中的一个元素变为响应式,并且保持其他东西“固定”在一个位置和/或尺寸。
这时候嵌套 svg
就很有用。 svg
元素有独立于它父元素的坐标系,它可以有独立的
viewBox 和
preserveAspectRatio
属性,你可以任意修改里面内容的尺寸和位置。
所以,要让一个元素更加灵活,我们可以把它包裹在
<svg> 元素中,并且给 svg
一个弹性的宽度来适应最外层SVG的宽度,然后声明
preserveAspectRatio=”none”
这样的话里面的图形会扩展和拉伸到容器的宽度。
注意 svg
可以多层嵌套,但是为了让事情简洁,我在这篇文章里只嵌套一层深度。
为了演示嵌套
svg 如何发挥作用,让我们来看一些例子。
例子试想我们有如下的SVG:上述SVG是响应式的。
改变屏幕的尺寸会导致整个SVG图形根据需要做出反应。
下面的截图展示了拉伸页面的结果,以及SVG如何变得更小。
注意SVG的内容如何根据SVG视窗和相互之间保持它们的初始位置。
使用嵌套SVG,我们将改变这个情况。
我们可以对SVG中每个独立的元素根据SVG视窗声明一个位置,所以随着SVG
视窗尺寸的改变(即最外层
svg 的改变),每个元素独立于其他元素发生改变。
注意,在这个时候,你需要熟悉SVG
viewport,
viewBox
, 和 preserveAspectRatio
是如何生效的。
我们将要创建一个效果,当屏幕尺寸变化时,蛋壳的上部分移动使得其中的可爱的小鸡显示出来,如下图所示:为了达到这个效果,蛋的上半部分必须和其他部分分离出来单独包含一个自己的
svg 。
这个 svg
包含框会有一个ID
upper-shell
。 然后,我们保证新的
svg#upper-shell
和外层SVG有一样的高度和宽度。
可以通过在
svg 上声明
width=”100%”
height=”100%”
或者不声明任何高度和宽度来实现。
如果内层SVG上没有声明任何宽高,它会自动扩展为外层SVG宽高的
100%
。 最终,为了确保上壳被“抬”起或定位在
svg#upper-shell
顶部的中心,我们将使用适当的
preserveAspectRatio
值来确保viewBox被定位在视窗的顶部中心-值是
xMidYMin
。 SVG图形的代码如下:
<svg version=”1.1″ xmlns=”http://www.w3.org/2000/svg” xmlns: xlink=”http://www.w3.org/1999/xlink”>
<!– … –>
<svg
viewBox=”0 0 315 385″
preserveAspectRatio=”xMidYMid meet”>
<!– the chicken illustration –>
<g
id=”chicken”>
<!– … –>
</g>
<!– path forming the lower shell –>
<path id=”lower-shell” fill=”url(#gradient)”
stroke=”#000000″stroke-width=”1.5003″d=”…”
/> </svg>
<svg id=”upper-shell” viewBox=”0 0 315 385″
preserveAspectRatio=”xMidYMin meet”>
<!– path forming the upper shell –>
<path id=”the-upper-shell” fill=”url(#gradient)”
stroke=”#000000″stroke-width=”1.5003″
d=”…”/> </svg>
</svg> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<svg
version=”1.1″xmlns=”http://www.w3.org/2000/svg”
xmlns:xlink=”http://www.w3.org/1999/xlink”>
<!– … –>
<svg viewBox=”0 0 315 385″ preserveAspectRatio=”xMidYMid meet”>
<!– the chicken illustration –>
<g id=”chicken”>
<!– … –>
</g>
<!– path forming the lower shell –>
<path id=”lower-shell” fill=”url(#gradient)”
stroke=”#000000″stroke-width=”1.5003″
d=”…”/> </svg>
<svg id=”upper-shell” viewBox=”0 0 315 385″
preserveAspectRatio=”xMidYMin meet”>
<!– path forming the upper shell –>
<path id=”the-upper-shell” fill=”url(#gradient)”
stroke=”#000000″stroke-width=”1.5003″
d=”…”/> </svg>
</svg>这个时候,注意在嵌套 svg#upper-shell 上声明的 viewBox
和最外层 svg 有相同的值(在它被移除之前)。我们用相同的 viewBox
值我原因就是这样,SVG在大屏幕上保持最初的样子。所以,这件事是这样的:我们开始一个SVG-在我们的例子中,这是一张里面藏着一个小鸡的带裂纹的蛋。然后,我们创建了另一“层”并把上部分的壳放在里面-这一层通过使用嵌套
svg 创建。嵌套 svg 和外层 svg 的尺寸和 viewBox 一样。最终,内层SVG的viewBox被设置成不管屏幕尺寸是多少都“固定”在viewport的顶部-这确保了当屏幕尺寸很窄时SVG被拉长,上层的壳被向上举起,因此展示出“隐藏”在里面的小鸡。一旦屏幕尺寸拉伸,SVG被拉长,使用
preserveAspectratio=”xMidYMin meet” 把包含上部分壳的viewBox被定位到viewport的顶部。点击下面按钮来查看在线SVG。记住改变屏幕尺寸再看SVG变化。在线案例嵌套或”分层”SVG使你可以根据改变的视窗定位SVG的一部分,在保持元素宽高比的情况下。所以图片可以在不扭曲内容元素的情况下自适应。如果我们想要整个鸡蛋剥离显示出小鸡,我们可以单独用一个
svg 层包含下部分壳, viewBox 也相同。确保下部分壳向下移动并固定在视窗的底部中心,我们使用
preserveAspectRatio=”xMidYMax meet” 来定位。代码如下:
<svg
version=”1.1″xmlns=”http://www.w3.org/2000/svg”
xmlns:xlink=”http://www.w3.org/1999/xlink”>
<svg id=”chick” viewBox=”0 0 315 385″
preserveAspectRatio=”xMidYMid meet”>
<!– the chicken illustration –>
<g id=”chick”>
<!– … –>
</g>
</svg>
<svg id=”upper-shell” viewBox=”0 0 315 385″
preserveAspectRatio=”xMidYMid meet”>
<!– path forming the upper shell –>
<path id=”the-upper-shell” fill=”url(#gradient)”
stroke=”#000000″stroke-width=”1.5003″
d=”…”/> </svg>
<svg id=”lower-shell”
viewBox=”0 0 315 385″preserveAspectRatio=”xMidYMax meet”>
<!– path forming the lower shell –>
<path id=”the-lower-shell” fill=”url(#gradient)”
stroke=”#000000″stroke-width=”1.5003″
d=”…”/> </svg>
</svg> 1 2 3 4 5 6 7 8 9 10 11 12 13 14
15 16 17 18
<svg version=”1.1″ xmlns=”http://www.w3.org/2000/svg”
xmlns:xlink=”http://www.w3.org/1999/xlink”>
<svg id=”chick” viewBox=”0 0 315 385″
preserveAspectRatio=”xMidYMid meet”>
<!– the chicken illustration –>
<g id=”chick”>
<!– … –>
</g>
</svg>
<svg id=”upper-shell” viewBox=”0 0 315 385″
preserveAspectRatio=”xMidYMid meet”>
<!– path forming the upper shell –>
<path id=”the-upper-shell”
fill=”url(#gradient)”stroke=”#000000″
stroke-width=”1.5003″d=”…”/> </svg>
<svg id=”lower-shell”
viewBox=”0 0 315 385″preserveAspectRatio=”xMidYMax meet”>
<!– path forming the lower shell –>
<path id=”the-lower-shell”
fill=”url(#gradient)”stroke=”#000000″
stroke-width=”1.5003″d=”…”/> </svg>
</svg>每个 svg 层/viewport等于最外层 svg 宽高的100%。所以我们基本有了三个副本。每层包含一个元素-上部分壳,下部分壳,或小鸡。三层的
viewBox 是相同的,只有 preserveAspectRatio
不同。当然,在这个例子里,一开始的图形中小鸡隐藏在蛋里,随着屏幕变小才显示出来。然而,你可以做一些不一样的:你可以开始在小屏幕上创建一个图形,然后在大屏幕上显示一些东西;即当
svg 变宽时才有更多垂直空间来展示元素。你可以更有创造性,根据不同屏幕尺寸来显示和隐藏元素-使用媒体查询-把新元素通过特定方式定位来达到特定的效果。想象力是无穷的。同时注意嵌套
svg 不需要和容器 svg 有相同的宽高;你可以声明宽高并且限制
svg 内容,超出边界裁切-这都取决于你想要达到什么效果。使用嵌套SVG使元素流动在保持宽高比的情况下定位元素,我们可以使用嵌套
svg 只允许特定元素流动-可以不保持这些特定元素的宽高比。例如,如果你只想SVG中的一个元素流动,你可以把它包含在一个
svg 中,并且使用 preserveAspectRatio=”none”
来让这个元素扩展始终撑满这个视窗的宽,并且保持宽高比和像我们在之前例子中做的一样定位其他元素。
<svg>
<!– … –>
<svg viewBox=”..” preserveAspectRatio=”none”>
<!– this content will be fluid –>
</svg>
<svg viewBox=”..” preserveAspectRatio=”..”>
<!– content positioned somewhere in the viewport –>
</svg>
<!– … –>
</svg> 1 2 3 4 5 6 7 8 9 10
<svg>
<!– … –>
<svg viewBox=”..” preserveAspectRatio=”none”>
<!– this content will be fluid –>
</svg>
<svg viewBox=”..” preserveAspectRatio=”..”>
<!– content positioned somewhere in the viewport –>
</svg>
<!– … –>
</svg>Jake Archibald创建了一个简单实用的嵌套SVG使用案例:一个简单的UI可以包含定位在最外层
svg 角落的元素,并且保持宽高比,UI的中间部分浮动并且根据svg宽度改变进行拉伸。你可以在这里查看。确保你在开发工具里检查代码来选取和想象不同viewbox和svg使用的效果。其他建立新视窗的方法svg
不是唯一能在SVG中创建新视窗的元素。在下面部分,我们会讨论使用其他SVG元素创建新视窗的方法。使用
<use> ing
<symbol> 建立一个新的视窗symbol 元素会定义新视窗,无论它什么时候被
use 元素实例化。symbol 元素的使用可以参考
use 元素中的 xlink:href 属性:
<svg>
<symbol id=”my-symbol” viewBox=”0 0 300 200″>
<!– contents of the symbol –>
<!– this content is only rendered when `use`d –>
</symbol>
<use xlink: href=”#my-symbol”
x=”?”y=”?”width=”?”
height=”?”>
</svg> 1 2 3 4 5 6 7
<svg>
<symbol id=”my-symbol” viewBox=”0 0 300 200″>
<!– contents of the symbol –>
<!– this content is only rendered when `use`d –>
</symbol>
<use xlink : href=”#my-symbol”
x=”?”y=”?”width=”?”
height=”?”>
</svg>上面值中的问号表示这些值也许没有声明-如果 x 和
y 没有声明,默认值为 0 ,也不需要声明宽高。看到了吧,当你
use 一个 symbol 元素,然后使用开发工具检查DOM,你不会看到
use 标签中 symbol 的内容。因为use的内容在shadow
tree里被渲染,如果你在开发工具中允许shadow
DOM显示你就能看到。当 symbol 被使用时,它被深度克隆到生成的shadow
tree中,例外是 symbol 被 svg 替换。这个生成的
svg 总是有明确的宽高。如果宽高的值在 use
元素上,这些值会被转换生成 svg 。如果属性宽和/或高没有声明,生成的
svg 元素会使用这些值的100%。因为我们在DOM中使用了
svg ,并且因为这个 svg 实际上包含在外层
svg 中,我们遇到的嵌套 svg 的状况和我们在之前一章讨论到的并没有多少不一样-嵌套的
svg 形成了一个新的 viewport 。嵌套
svg 的 viewBox 是在 symbol 元素上声明的
viewBox 。( symbol 元素接受 viewBox
元素值。更多信息,阅读这篇文章:Structuring,
Grouping, and Referencing
in SVG – The , , and Elements)所以我们现在有了一个新的viewport,尺寸和位置可以使用元素(
x , y , width , height )声明,
viewBox 值可以在 symbol 元素上声明。
symbol 的内容随后再这个视窗和viewBox中被渲染和定位。最后,
symbol 元素也接收 preserveAspectratio
属性值,你可以在由 use 建立的新视窗中定位 viewBox
。这很清楚,不是吗?你可以像我们在之前的部分里一样控制新创建的嵌套
svg 。Dirk Weber 也创建了一个使用嵌套SVG和
symbol 元素来模仿css border images的表现。你可以在这里查看文章。参考
<image> 中的SVG image建立一个新视窗images
元素表明整个文件的内容被渲染到一个当前用户坐标系中给定的长方形。
image 元素可以代表图片文件例如PNG或JPEG或者有”image/svg+xml”的MIME类型的文件。代表SVG文件的
image 元素会导致建立一个临时新视窗因为定义相关资源有
svg 元素。
<image xlink:
href=”myGraphic.svg”
x=”?”y=”?”width=”?”
height=”?”preserveAspectRatio=”?”
/> 1
<image xlink : href=”myGraphic.svg”
x=”?”y=”?”width=”?”
height=”?”preserveAspectRatio=”?”
/>
<image> 元素接收许多属性,其中一些属性-和这篇文章有关的-是
x 和 y 位置属性, width
和 height 属性以及 preserveAspectratio
。通常,SVG文件会包含一个根
<svg>
元素;这个元素也许声明位置和尺寸,另外也许有
viewBox 和 preserveAspectratio
值。当一个 image 元素代表SVG图片文件,根svg的
x , y , width
和 height 属性被忽略。除非
image 元素上的 preserveAspectRatio
值以“defer”开头,根元素上的
preserveAspectRatio
值在代表SVG图片时也被忽略。然而相关
image 元素上的 preserveAspectRatio
属性定义SVG图片内容如何适应视窗。评估被参考内容定义的
preserveAspectRatio
属性时使用 viewBox
属性值。对于明确定义的viewBox内容(例如,最外层元素上有
viewBox 属性的SVG文件)值应该被使用。对于大多数值(PING,JPEG),图片边界应该被使用(即
image 元素有隐含的尺寸为’0
0 raster-image-width
raster-image-height’的
viewBox )。如果值不全的话(例如,外层的svg元素没有
viewbox 属性的SVG文件)
preserveAspectRatio
值被忽略,只有视窗 x &
y 属性引起的移动才用来显示内容。例如,如果一个image元素代表PNG或JPEG并且
preserveAspectRatio=”xMinYMin
meet” ,那么栅格的宽高比会保持,栅格会在保证整个栅格适应视窗的情况下尽可能放大尺寸,栅格的左上角会和由
image 元素上 x ,
y , width 和 height
定义的视窗的左上角对齐。如果
preserveAspectRatio
的值是“none”那么图片的宽高比不会保持不变。图片会自适应,栅格的左上角和坐标系(
x , y )完全对齐,栅格的右下角和坐标系(
x + width , y
+ height )完全对齐。使用
<iframe> 建立新视窗代表SVG文件的
iframe 元素建立新坐标系的情况类似于上述解释的
image 元素的情况。
iframe 元素也可以有
x , y , width
和 height
属性,除了它自身的
preserveAspectratio
之外。使用
<foreignObject>
建立新视窗foreignObject
元素建立一个新的viewport来渲染这个元素的内容。foreignObject
标签允许你把非SVG内容添加到SVG文件中。通常,
foreignObject
的内容被认为不同于命名空间。例如,你可以把一些html放到SVG元素的中间。foreignObject
接收属性包括
x , y
, height
和 width
,用来定位对象和调整尺寸,创建用于呈现它里面所引用的内容的范围。有需要关于
foreignObject
元素的要说因为它给内容创建了新的viewport。如果你感兴趣,可以查看MDN
entry或者在The
Nitty
Gritty
Blog上查看Christian
Schaeffer创建的实际使用例子。结束语建立新的viewports和坐标系-像上述提到的一样通过嵌套
svg 和其他元素-允许你控制SVG的部分内容而通过其他方式你可能没法一样控制。在写这片文章以及思考例子和使用情况的整个过程中,我一直在思考嵌套SVG如何让我们在处理SVG时能更好控制并有更灵活的方式。自适应SVG可以通过简洁的代码创建,在SVG中可以创建独立于其他元素的流动元素,用来模拟css
border
images来在高分屏上定义背景。你是否已经在SVG中使用嵌套视窗来创建有趣的例子了呢?你能否相处更多有创意的例子呢?这篇文章总结了“理解SVG坐标系和变换”这个系列。下一步,我们会讨论动画,甚至更多!敬请期待,感谢你的阅读!
评论前必须登录!
注册