2018-09-17_EstablishingNewViewports

理解SVG坐標系和變換(第三部分)-建立新視窗

(本文轉自w3cplus,這里僅修正了部分個人認為翻譯不恰當之處;下面是譯文鏈接,英文原文鏈接在結尾處提供)

在SVG繪圖的任何一個點,你可以通過嵌套<svg>元素或者其他元素來建立新的viewport和用戶坐標系。在這篇文章中,我們將看一下我們如何這樣做,以及這樣做如何幫助我們控制SVG元素并讓它們變得更加flexible(and/or fluid)。

這是SVG坐標系和變換系列的第三篇也是最后一篇文章。在第一篇中,包括了任何要理解SVG坐標系統基礎的需要知道的內容;更具體的是, SVG viewport, viewBox和 preserveAspectRatio屬性。在第二篇文章里,你可以了解到任何你需要了解的關于SVG系統變換的內容。

通過這篇文章,我假定你已經讀了這個系列的第一部分關于SVG viewport, viewBox 和 preserveAspectRatio 屬性的內容。在閱讀這篇文章之前你不需要讀第二篇關于坐標系變換的內容。

嵌套<svg>元素

第一部分我們討論了<svg>元素如何為SVG畫布內容建立一個viewport。在SVG繪圖中的任何一點上,都可以通過在另一個< SVG >中包含< SVG >元素來創建一個新的視圖,其中包含所有包含的圖形。通過建立一個新的viewport,您還隱式地建立了一個新的viewport坐標系和一個新的用戶坐標系。

例如,試想有一個<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>

第一件需要注意的是內部<svg>元素不需要聲明一個命名空間xmlns因為默認和外層<svg>的命名空間相同。當然,即使是外部<svg>元素,如果它是被嵌入在HTML5文檔中不需要命名空間。

你可以使用一個嵌套的SVG來把元素組合在一起然后在父SVG中定位它們。現在,你也可以把元素組合在一起并且使用<g>group組來定位-通過把元素包括在一組<g>元素a group <g> element中。你可以使用transform屬性在畫布中定位它們。然而,使用一個<svg>元素必然優于使用<g>元素。使用x和y坐標來定位,在許多情況下,比使用變換更加方便。另外,<SVG>元素接受width和height, <g>元素不行。這意味著,<svg>元素可能并不總是需要或必須的,因為這會導致創建一個新的視圖和坐標系統,您可能不需要或不希望這樣做。

通過給<svg>元素指定寬高值,您可以將其中的內容限制在由x、y、width和height屬性定義的viewport的范圍內。任何超出這些范圍的內容都將被裁切。

如果你不聲明x和y屬性,它們默認是0。如果你不聲明height和width屬性,會是父SVG寬度和高度的100%。

此外,指定一個非默認的用戶坐標系統也會影響內部<svg>.

為內層<svg>內部的子元素指定的百分比值將會相對它(即內層<svg>)計算,而不是相對于外層<svg>。(譯者注:請結合示例理解)例如,下面的代碼會導致內層SVG等于400單位,里面的長方形是200個單位:

<svg width="800" height="600">
<svg width="50%" ..>
<rect width="50%" ... />
</svg>
</svg>

如果最外層<svg>的寬度設置為100%(例如,如果它在一個文檔中內聯或者你想要它可以流動),然后,內部SVG將根據需要進行擴展和收縮,以保持寬度為外部SVG的一半——這非常強大。

嵌套SVG在給SVG畫布中的元素增加靈活性和擴展性時尤其有用。我們知道,使用viewBox值和preserveAspectRatio,我們已經可以創建響應式SVG。最外層的寬度可以設置成100%來確保它擴展拉伸到它的容器(或頁面)擴展或拉伸。然后通過使用viewBox值和 preserveAspectRatio,我們可以保證SVG畫布可以自適應viewport中的改變(最外層svg)。我在CSSConf演講的幻燈片中寫到了關于響應式SVG的內容。你可以在這里查看這個技術。

然而,當我們像這樣創建一個響應式SVG,整個畫布以及所有繪制在上面的元素都會有反應并且同時改變。但有時候,你只想讓圖形中的一個元素變為響應式,并且保持其他東西“固定”在一個位置和/或尺寸。這時候嵌套svg就很有用。

svg元素有獨立于它父元素的坐標系,它可以有獨立的viewBox和preserveAspectRatio屬性,你可以任意修改里面內容的尺寸和位置。

所以,要讓一個元素更加靈活,我們可以把它包裹在元素中,并且給svg一個彈性的寬度來適應最外層SVG的寬度,然后聲明preserveAspectRatio="none"這樣的話里面的圖形會擴展和拉伸到容器的寬度。注意svg可以多層嵌套,但是為了讓事情簡潔,我在這篇文章里只嵌套一層深度。

為了演示嵌套svg如何發揮作用,讓我們來看一些例子。

例子

試想我們有如下的SVG:

svg-nesting-example-1.png

上述SVG是響應式的。改變屏幕的尺寸會導致整個SVG圖形根據需要做出反應。下面的截圖展示了拉伸頁面的結果,以及SVG如何變得更小。注意SVG的內容如何根據SVG視窗和相互之間保持它們的初始位置。

svg-nesting-example-1-resized.png

使用嵌套SVG,我們將改變這個情況。我們可以對SVG中每個獨立的元素根據SVG視窗聲明一個位置,所以隨著SVG 視窗尺寸的改變(即最外層svg的改變),每個元素獨立于其他元素發生改變。

注意,在這個時候,你需要熟悉SVG viewport, viewBox, 和preserveAspectRatio是如何生效的。

我們將要創建一個效果,當屏幕尺寸變化時,蛋殼的上部分移動使得其中的可愛的小雞顯示出來,如下圖所示:

svg-nesting-example-1-new.png

為了達到這個效果,蛋的上半部分必須和其他部分分離出來單獨包含一個自己的svg。這個svg包含框會有一個ID upper-shell

然后,我們保證新的svg#upper-shell和外層SVG有一樣的高度和寬度。可以通過在svg上聲明width="100%"height="100%"或者不聲明任何高度和寬度來實現。如果內層SVG上沒有聲明任何寬高,它會自動擴展為外層SVG寬高的100%。

最終,為了確保上殼被“抬”起或定位在svg#upper-shell頂部的中心,我們將使用適當的preserveAspectRatio值來確保viewBox被定位在viewport的頂部中心-值是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>

我已經刪除了與本文相關的部分,比如用于給蛋殼上色的漸變和形成形狀的路徑,只是為了在示例代碼中簡潔起見。

此時,請注意嵌套的svg#upper shell中指定的viewBox的值與最外層的svg的值相同(在去掉它之前)。我們使用相同的viewBox值的原因是,SVG在大屏幕上保持了原來的外觀。

所以,事情是這樣的:我們從一個svg開始——在我們的例子中,是一個破裂的雞蛋,里面藏著一只雞。然后,我們創建另一個“層”,并將上層shell提升到—這個層是通過使用嵌套svg創建的。嵌套的svg具有與外部svg相同的維度和相同的視圖框。最后,內在的viewBox SVG將“堅持”窗口頂部的不管什么屏幕大小—確保,當屏幕尺寸縮小和SVG是細長的,上殼向上將被取消,從而顯示canvas"身后"的小雞。

svg-nesting-example-1-layered.png

一旦屏幕尺寸拉伸,SVG被拉長,使用preserveAspectratio="xMidYMin meet"把包含上部分殼的viewBox被定位到viewport的頂部。

svg-nesting-example-1-viewbox.png

點擊下面按鈕來查看在線SVG。記住改變屏幕尺寸再看SVG變化。

在線案例

嵌套或"分層"SVG使你可以根據改變的視窗定位SVG的一部分,在保持元素寬高比的情況下。所以圖片可以在不扭曲內容元素的情況下自適應。

如果我們想要整個雞蛋剝離顯示出小雞,我們可以單獨用一個svg層包含下部分殼,viewBox也相同。確保下部分殼向下移動并固定在視窗的底部中心,我們使用preserveAspectRatio="xMidYMax meet"來定位。代碼如下:

<meta charset="utf-8">

<svg version="1.1" xmlns="[http://www.w3.org/2000/svg](http://www.w3.org/2000/svg)" xmlns:xlink="[http://www.w3.org/1999/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-nesting-example-1-2.png

當然,在這個例子里,一開始的圖形中小雞隱藏在蛋里,隨著屏幕變小才顯示出來。然而,你可以做一些不一樣的:你可以開始在小屏幕上創建一個圖形,然后在大屏幕上顯示一些東西;即當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>

Jake Archibald創建了一個簡單實用的嵌套SVG使用案例:一個簡單的UI可以包含定位在最外層svg角落的元素,并且保持寬高比,UI的中間部分浮動并且根據svg寬度改變進行拉伸。你可以在這里查看。確保你在開發工具里檢查代碼來選取和想象不同viewbox和svg使用的效果。

其他建立新視窗的方法

svg不是唯一能在SVG中創建新視窗的元素。在下面部分,我們會討論使用其他SVG元素創建新視窗的方法。

使用<use>ing<symbol> 建立一個新的視窗

只要由<use>元素實例化<symbol>元素,它就會定義一個新的視圖。

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>

上面值中的問號表示這些值也許沒有聲明-如果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 )

所以我們現在有了一個新的viewport,尺寸和位置可以使用元素(x,y, width, height)聲明,viewBox值可以在symbol元素上聲明。symbol的內容隨后再這個視窗和viewBox中被渲染和定位。

最后,symbol元素也接收preserveAspectratio屬性值,你可以在由use建立的新視窗中定位viewBox。這很清楚,不是嗎?你可以像我們在之前的部分里一樣控制新創建的嵌套svg。

Dirk Weber 也創建了一個使用嵌套SVG和symbol元素來模仿CSS border images的表現。你可以在這里查看文章。

通過在<image>中引用SVG圖像來建立新的視圖

images元素表明整個文件的內容被渲染到一個當前用戶坐標系中給定的長方形。image元素可以代表圖片文件例如PNG或JPEG或者有"image/svg+xml"的MIME類型的文件。

代表SVG文件的image元素會導致建立一個臨時新視窗因為定義相關資源有svg元素。

<image xlink:href="myGraphic.svg" x="?" y="?" width="?" height="?" preserveAspectRatio="?" />

<image>元素接收許多屬性,其中一些屬性-和這篇文章有關的-是x和y位置屬性,width和height屬性以及preserveAspectratio。

通常,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)完全對齊。

Establishing a new viewport using iframe

An iframe element that references an SVG file establishes new viewport similar to the situation of image element explained above. An iframe element can also have x, y, width, and height attributes, in addition to its own preserveAspectratio.

Establishing a new viewport using <foreignObject>

A foreignObject element creates a new viewport for rendering the content that is within the element.

The foreignObject tag allows you to add non-SVG content into an SVG file. Usually, the contents of foreignObject are assumed to be from a different namespace. For example, you could drop some HTML in the middle of an SVG element.

The foreignObject element accepts attributes, among which are x, y, height, and width, which are used to position the object and size it, creating the bounds used to render the contents referenced inside it.

There is a lot to say about the foreignObject element besides its creation of a new viewport for its content. If you’re interested, you can check the MDN entry or check this practical use case by Christian Schaeffer on The Nitty Gritty Blog.

Wrapping Up

Establishing new viewports and coordinate systems—be that by nesting svgs or another element from the ones mentioned above—allows you to control parts of the SVG that you would otherwise not be able to control the same way.

The entire time that I was working on this article and thinking of demos and use cases, all I kept thinking of is how nesting SVGs can give us finer control and flexibility for when we’re dealing with SVGs. Adaptive SVGs can be created with neat effects, fluid elements inside SVGs that are independent of the other elements on the page are possible, mimicing CSS border images for crispier backgrounds on high-resolution screens, and so much more.

Have you created any interesting examples using nested viewports in SVG? Can you think of more creative examples?

This article concludes the series of “Understanding SVG Coordinate Systems & Transformations”. Next up, we’ll be diving into animations, and more! Stay tuned, and thank you for reading!

Find similar articles under: #svg

原文鏈接:Understanding SVG Coordinate Systems and Transformations (Part 3) — Establishing New Viewports

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容

  • 理解SVG坐標系和變換(第二部分)transform (本文轉自w3cplus,這里僅修正了部分個人認為翻譯不恰當...
    王策北閱讀 278評論 0 1
  • 理解SVG坐標系和變換(第一部分)-viewport,viewBox,和preserveAspectRatio (...
    王策北閱讀 355評論 0 1
  • 1. 前言 前端圈有個“梗”:在面試時,問個css的position屬性能刷掉一半人,其中不乏工作四五年的同學。在...
    YjWorld閱讀 4,504評論 5 15
  • 天氣好的時候,陽光總是給它增添光亮與色彩 如果你有不順心的時候,四處去看看,那些往往你一腳就能毀掉的風景,可不...
    紅宛閱讀 131評論 0 1
  • 禮物 文/張渝涵 在我的記憶中,我收到了很多禮物,包括我爸爸的戰友和親戚朋友,都送了我不少的禮物,但是,在我記憶深...
    童聲童話閱讀 282評論 0 2