[譯]《Smashing》: 用 CSS 形狀打造高階排版

西樓聽雨發表於2019-02-27

原文Take A New Look At CSS Shapes
作者Rachel Andrew 發表時間:september 4, 2018
譯者:西樓聽雨 發表時間: 2018/9/16
(轉載請註明出處)

摘要:在本文中,我們將對 CSS 形狀的概念,以及如何用圖片、漸變和基礎形狀來建立非矩形類的形狀做一個講解。另外我們還會講到 Firefox 的新工具是如何讓我們編輯形狀變得簡單的。

展開原文 CSS Shapes Level 1 has been available in Chrome and Safari for a number of years, however, this week it ships in a production version of Firefox with the release of Firefox 62 — along with a very nice addition to the Firefox DevTools to help us work with Shapes. In this article, I’ll take a look at some of the things you can do with CSS Shapes. Perhaps it’s time to consider adding some curves to your designs?

CSS 形狀第一階段規範已經在 Chrome 和 Safari 中實現了有好幾年了,而隨著 Firefox 62 在本週釋出,Firefox 也已經在生產版本中支援了——另外隨之釋出的還有一個很棒的開發者工具功能,它對我們使用形狀可以帶來幫助。在本文中,我們將看下一可以用 CSS 形狀做哪些事情。也許是時候考慮給我們的設計新增一些曲線效果了!

什麼是 CSS 形狀(CSS Shapes)?

The CSS Shapes specification Level 1 defines three new properties:

CSS 形狀規範的第一階段 (CSS Shapes Level 1) 定義了三個新的屬性:

  • shape-outside
  • shape-image-threshold
  • shape-margin

展開原文 The purpose of this specification is to allow content to flow around a non-rectangular shape, something which is quite unusual on our boxy web. There are a few different ways to create shapes, which we will have a look at in this tutorial. We will also have a look at the Shape Path Editor, available in Firefox, as it can help you to easily understand the shapes on your page and work with them.

In the current specification, shapes can only be applied to a float, so any shapes example needs to start with a floated element. In the example below, I have a PNG image with a transparent background in which I have floated the image left. The text that follows the image now flows around the right and bottom of my image.

What I would like to happen is for my content to follow the shape of the opaque part of the image, rather than follow the line of the physical image file. To do this, I use the shape-outside property, with the value being the URL of my image. I’m using the actual image file to create a path for the content to flow around.

這個規範的制定,目的是為了讓內容的排布可以支援環繞著非矩形類形狀進行,即那些和我們 Web 的盒子模型極大不同的形狀。建立形狀有多種方式,我們會在這篇教程中講到。另外我們還會對 Shape Path Editor ——可在 Firefox 中找到——做一下介紹,因為它可以幫助我們理解和使用形狀。

在當前版本的規範中,規定了形狀只能被應用在浮動元素上。在下面的這個例子中,我放了一張背景透明的 PNG 圖片,並將其浮動到左邊。其後的文字隨之排布在了其右側和底部。

在這例子中,我真正希望的結果是,我的內容可以圍繞圖片中非透明部分的形狀進行排布,而不是這個圖片檔案的物理邊線。所以,為了實現這種效果,我使用的是 shape-outside 屬性,並把這個圖片的 URL 作為其值。這裡我使用的就是圖片檔案來為我的內容建立環繞路徑的。

[譯]《Smashing》: 用 CSS 形狀打造高階排版

展開原文 Note that your image needs to be CORS compatible, so hosted on the same server as the rest of your content or sending the correct headers if hosted on a CDN. Browser DevTools will usually tell you if your image is being blocked due to CORS.

This method of creating shapes uses the alpha channel of the image to create the shape, as we have a shape with a fully transparent area, then all we need do is pass the URL of the image to shape-outsideand the shape path follows the line of the fully opaque area.

注意,你的圖片須相容 CORS,所以需要把圖片放在和你內容相同的伺服器下,或者假如放在 CDN 上的話,需要確保響應正確的 HTTP 頭部。如果你的圖片因為 CORS 被遮蔽了,可以在瀏覽器的開發者工具中看到。

(譯註:CORS,全稱 Cross-Origin Resource Sharing;跨源性資源共享。這裡的“圖片”指的是 shape-outside 屬性中的 URL,而不是指 img 標籤——它是無需相容 CORS 的 )

這種建立形狀的方法,使用的是圖片的 alpha 通道。因為我們這張圖片裡的形狀之外都是完全透明的區域,所以我們只需把它 URL 給到 shape-outside 屬性,即可實現將非透明區域的邊線所形成的路徑作為形狀。

設定外邊距

展開原文 To push the line of the text away from the image we can use the `shape-margin` property. This creates a margin between the line of the shape and the content running alongside it.

為了將文字從圖片上推開,我們可以使用 shape-margin 屬性,它可以在形狀及其周圍的內容之間設定一個邊界。

[譯]《Smashing》: 用 CSS 形狀打造高階排版

利用偽類充當形狀載體 (Using Generated Content For Our Shape)

展開原文 In the case above, we have the image displayed on the page and then the text curved around it. However, you could also use an image as the path for the shape in order to create a curved text effect without also including the image on the page. You still need something to float, however, and so for this, we can use Generated Content.

上面的例子中,我們將圖片展示在頁面中,並使得文字環繞著它彎曲。其實,你也可以做到在不展示那張圖片的情況的同時使用那張圖片的形狀作為環繞路徑來獲得同樣的效果。不過要做到這種效果,我們仍然需要有一個浮動的元素,為此,我們可以用生成的內容 (Generated Content;[譯註] 這裡指偽類) 來充當。

[譯]《Smashing》: 用 CSS 形狀打造高階排版

展開原文 In this example, we have inserted some generated content, floated it left, given it a width and a height and then used `shape-outside` with our image just as before. We then get a curved line against the whitespace, but no visible image.

在這個例子中,我們通過插入生成的內容,並將其浮動到左側,給其設定寬度和高度,然後使用之前的圖片作為 shape-outside 。這樣我們就達到了在沒有可見的圖片的情況下,仍然獲得曲線性地圍繞空白區域的效果。

使用漸變作為形狀

展開原文 A CSS gradient is just like an image, which means we can use a gradient to create a shape, which can make for some interesting effects. In this next example, I have created a gradient which goes from blue to transparent; your gradient will need to have a transparent or semi-transparent area in order to use shapes. Once again, I have used generated content to add the gradient and am then using the gradient in the value for `shape-outside`.

Once the gradient becomes fully transparent, then the shape comes into play, and the content runs along the edge of the gradient.

CSS 漸變就相當於一張圖片,也就是說我們可以用其來建立形狀,這樣我們就可以製作出一些有趣的特效。在下面這個例子中,我建立了一個藍色到透明的漸變(要使用形狀,需要有一定的透明區域或半透明區域)。這次我同樣使用了生成的內容作為漸變的載體和 shap-outside 的值。

在漸變過度到全透明的地方,就會形成形狀,也是內容所環繞的邊界。

[譯]《Smashing》: 用 CSS 形狀打造高階排版

使用 shape-image-threshold 來支援環繞半透明圖片

展開原文 So far we have looked at using a completely transparent part of an image or of a gradient in order to create our shape, however, the third property defined in the CSS Shapes specification means that we can use images or gradients with semi-opaque areas by setting a threshold. A value for `shape-image-threshold` of `1` means fully opaque while `0` means fully transparent.

A gradient like our example above is a great way to see this in action as we can change the shape-image-threshold value and move the line along which the text falls to more opaque areas or more transparent areas. This property works in exactly the same way with an image that has an alpha channel yet is not fully transparent.

到這裡為止,我們已經看到了如何使用圖片或漸變的全透明部分來建立形狀。其實,CSS 形狀規範中定義的這第三個屬性可以讓我們通過設定一個透明度閾值來使用圖片或漸變的半透明部分建立形狀。這個屬性的值為 1 時,表示完全不透明;值為 0 時,表示全透明。

要觀察它的效果,前一個例子中的漸變就是一個很好的物件,我們可以改變其 shape-image-threshold 屬性的值來使文字落在更加不透明的區域或更加透明的區域中的位置。對於有 alpha 通道的非全透明的圖片,這個屬性的效果完全一樣。

[譯]《Smashing》: 用 CSS 形狀打造高階排版

展開原文 This method of creating shapes from images and gradients is — I think — the most straightforward way of creating a shape. You can create a shape as complex as you need it to be, in the comfort of a graphics application and then use that to define the shape on your page. That said, there is another way to create our shapes, and that’s by using *Basic Shapes*.

上面這些建立形狀的方法,在我看來,是最直接的。你可以通過先在影象處理類軟體中按照你的需要建立任意複雜的形狀,然後在頁面中進行使用。其實我是想說還有另一種方式建立形狀,那就是使用基礎形狀 (Basic Shapes) 。

用基礎形狀建立 CSS 形狀

展開原文 The Basic Shapes are a set of predefined shapes which cover a lot of different types of shapes you might want to create. To use a basic shape, you use the basic shape *type* as a value for `shape-outside`. This type uses functional notation, so we have the name of the shape followed by brackets (inside which are some values for our shape).

“基礎形狀”是一系列預定義的形狀,它包含了許多你可能想要建立的各類形狀。使用基礎形狀時,我們使用的是基礎形狀的型別作為 shape-outside 的值。這種型別使用函式形式來表示的,所以他們的名字的尾部會有一對括號 (括號裡面是一些關於我們所需的形狀的值) 。

The options that you have are the following:

你可以使用的形狀型別有以下這些:

  • inset()
  • circle()
  • ellipse()
  • polygon()
展開原文 We will take a look at the `circle()` type first as we can use this to understand some useful things which apply to all shapes which use the basic shape type. We will also have a look at the new tools in Firefox for inspecting these shapes.

In the example below, I am creating the most simple of shapes: a circle using shape-outside: circle(50%). I’m using generated content again, and I have given the box a background color, and also added a margin, border, and padding to help highlight some of the concepts of using CSS Shapes. You can see in the example that the circle is created centered on the box; this is because I have given the circle a value of 50%. That value is the <shape-radius> which can be a length or a percentage. I’ve used a percentage so that the radius is half of the size of my box.

在下面的例子中,我建立是最簡單的形狀之一:一個圓,使用的是 shape-outside: circle(50%)。這次我還是使用生成的內容,並給了這個 box 一個背景色,還設定了一個外邊距、邊框和內邊距,以此來著重展示使用 CSS 形狀的一些概念。在這個例子中,你可以看到,這個圓居中在這個 box 中;這是因為我給了這個圓一個 50% 的值。這個值的定義為 <shape-radius> (譯註:形狀的半徑),它可以是一個長度值,也可以是一個百分比。

[譯]《Smashing》: 用 CSS 形狀打造高階排版

展開原文 This is a really good to time have a look at the shape that has been created using the Firefox Shape Path Editor. You can inspect the shape by clicking on the generated content and then clicking the little shape icon next to the property `shape-outside`; your shape will now highlight.

現在就是我們用 Firefox 的 Shape Path Editor 來檢視一下這個形狀的好時機。點選生成的內容,然後點選其 shape-outside 屬性旁的圖示,就可以將形狀高亮顯示出來。

The shape highlighted with a line

展開原文 You can see how the circle extends to the edge of the margin on our box. This is because the initial *reference box* used by our shape is `margin-box`. You already know something of reference boxes if you have ever added `box-sizing: border-box` to your CSS. When you do this, you are asking CSS to use the `border-box` and not the default `content-box` as the size of elements. In Shapes, we can also change which reference box is used. After any basic shape, add `border-box`to use the border to define the shape or `content-box` to use the edge of the content (inside the padding). For example:

你可以看到,這個圓的範圍觸及到了外邊距的外邊緣上,這是因為我們的形狀預設使用的參照盒子 (reference box) 是 margin-box。如果你之前有新增過 box-sizing: border-box,你其實就已經知道什麼是參照盒子,當你新增這個屬性時,就是在告訴 CSS 要使用 border-box 而不是預設的 content-box 作為元素的尺寸。在形狀的使用中,我們同樣可以調整其所使用的參照盒子。在任意基礎形狀之後,加上 border-box 表示使用邊框來定義形狀,加上 content-box 則使用內容區域 (在 padding 之內) 的邊緣的來定義。例如:

.content::before {
    content: "";
    width: 150px;
    height: 150px;
    margin: 20px;
    padding: 20px;
    border: 10px solid #FC466B;
    background: linear-gradient(90deg, #FC466B 0%, #3F5EFB 100%);
    float: left;
    circle(50%) content-box;
}
複製程式碼

You will see the circle appear to become much smaller. It is now using the width of the content — in this case the width of the box at 150px — rather than the margin box which includes the padding, border, and margin.

這樣,你就會發現這個圓看上去嚴重縮小了,它現在使用的是內容區域的寬度——本例中為 150px ——而不是包含了內邊距、邊框和外邊距的 margin-box。

A smaller circle is highlighted

Inspecting your element in Firefox DevTools will also show you the reference boxes so you can choose which might give you the best result with your particular shape.

在 Firefox 的開發者工具中對這個元素進行審視 (inspecting) ,會展示出它的各類參照盒子,據此你可以選擇出特定形狀下效果最好的那個。

Highlights showing the margin, border and padding

circle() 的位置值

展開原文 A second value can be passed to `circle()` which is a position; if you do not pass this value, it defaults to `center`. However, you can use this value to pull your circle around. In the next example, I have positioned the circle by using `shape-outside(50% at 30%)`; this changes where the center of the circle is positioned.

circle() 中的第二個值表示的是位置,如果你不指定的話,預設為 center。你可以利用這個值來任意調整你的圓所在的位置。在接下來的例子中,我使用 shape-outside(50% at 30%) (譯註:這明顯是原文的筆誤,正確的是 circle(50% at 30%)) 來設定這個圓的位置,它調整的是這個圓的中心點所處的位置。

[譯]《Smashing》: 用 CSS 形狀打造高階排版

clip-path

展開原文 Something useful to know is that the same `` values can be used as a value for `clip-path`. This means that after creating a shape, you can clip away the image or background color that extends outside of the shape. In the example below, I am going to do this with our example gradient background, so that we end up with a circle that has text curved around from our square box.

一個有用的知識是,基礎形狀同樣可以使用在 clip-path 上;也就是說在建立了一個形狀值後,你可以把超出了形狀之外的圖片和背景顏色部分剪掉。在下面這個例子中,我將用我們的漸變背景的例子來展示,我們會得到一個圍繞著彎曲了的文字的圓。

[譯]《Smashing》: 用 CSS 形狀打造高階排版

All of the above concepts can be applied to our other basic shapes. Now let’s have a quick look at how they work.

本段講的這些,對我們的其他基礎形狀同樣適用。現在讓我們來看下一他們是如何使用的。

INSET()

展開原文 The `inset()` value defines a rectangle. This might not seem very useful as a float is a rectangle, however, this value means that you can inset the content wrapping your shape. It takes four values for top, right, bottom, and left plus a final value which defines a border radius.

In the example below, I am using the values to inset the content on the right and bottom of the floated image, plus adding a border radius around which my content will wrap using shape-outside: inset(0 30px 100px 0 round 40px). You can see how the content is now over the background color of the box:

inset() 值定義的是一個矩形。這看上去好像沒什麼用,因為浮動元素本身就是矩形;其實它的真正的涵義是你可以將文字內斂 (inset) 進來。它接收四個引數,分別為頂部、右側,底部和左側,再加一個圓角半徑值。

在下面這個例子中,我使用 shape-outside: inset(0 30px 100px 0 round 40px) 來將內容從右側和底部內斂進來,在加上一個圓角半徑。現在,你可以看到這些內容將如何覆蓋到盒子的背景顏色上:

[譯]《Smashing》: 用 CSS 形狀打造高階排版

ELLIPSE()

展開原文 An ellipse is a squashed circle and as such needs two radii for x and y (in that order). You can then push the ellipse around just as with circle using the position value. In the example below, I am creating an ellipse and then using clip-path with the same values to remove the content outside of my shape.

橢圓就是被壓扁了的圓,所以我們需要有兩個半徑:x 和 y (建立時順序與此一致) 來建立,也同樣也可以像圓一樣通過位置值來任意調整位置。在下面這個例子中,我建立了一個橢圓並把相同的值使用在 clip-path 屬性上。

[譯]《Smashing》: 用 CSS 形狀打造高階排版

In the above example, I also used shape-margin to demonstrate how we can use this property as with our image generated shapes to push the content away.

在這個例子中,我還使用了 shape-margin 屬性,為的是演示如何配合使用這個屬性,將內容推開一定距離。

POLYGON()

展開原文 Creating polygon shapes gives us the most flexibility, as our shapes can be created with three or more points. The value passed to the polygon needs to be three or more pairs of values which represent coordinates.

It is here where the Firefox tools become really useful as we can use them to help create our polygon. In the below example, I have created a polygon with four points. In the Firefox DevTools, you can double-click on any line to create a new point, and double-click again to remove it. Once you have created a polygon that you are happy with, you can then copy the value out of DevTools for your CSS.

多邊形形狀的建立可以讓我們擁有最大的靈活性,因為我們的形狀可以用三個以上的點來建立。傳遞到多邊形中的值需要至少3對錶示座標的值。

這個時候 Firefox 就變得非常有用了,因為他可以幫助我們建立多邊形。下面這個例子中,我建立了一個由四個點組成的多邊形,在 Firefox 的開發者工具中,你可以在任意一條邊上雙擊來新增一個新的點,再次雙擊可以移出這個點。在你建立出了讓你滿意的多邊形後,你就可以將其從開發者工具中拷貝出來用到你的 CSS 中。

[譯]《Smashing》: 用 CSS 形狀打造高階排版

相容性問題 (FALLBACKS)

展開原文 As CSS Shapes are applied to a float, in many cases the fallback is that instead of seeing the content wrap around a shape, the content will wrap around a floated element (in the way that content has always wrapped around floats). Browsers ignore properties they do not understand, so if they don’t understand Shapes, it doesn’t matter that the `shape-outside` property is there.

Where you should take care would be in any situation where not having shapes could mean that content overlaid an area which made it difficult to read. Perhaps you are using Shapes to push content away from a busy area of a background image, for example. In that case, you should first make sure that your content is usable for the non-Shapes people, then use Feature Queries to check for support of shape-outside and overwrite that CSS and apply the shape. For example, you could use a margin to push the content away for non-Shapes visitors and remove the margin inside your feature query.

因為 CSS 形狀是應用在浮動元素上的,所以在大多數情況下出現相容性問題時,我們看的將會是內容圍繞著浮動元素 (就和一般情況下的一樣),而不是圍繞著形狀排布。瀏覽器會把他所不支援的屬性忽略掉,所以如果它們不支援形狀,把 shape-outside 屬性放上去也不會有什麼影響。

你真正需要考慮的問題是,在不支援形狀的情況下,會使得內容難以閱讀的內容排布情形。通常我們使用形狀,都會把內容隔開一定距離。在這種情形下,你應該首先確保那些不支援形狀的使用者,然後再使用特性查詢(Feature Query)來檢查 shape-outside 的支援情況,並進行 CSS 覆蓋和應用形狀。例如,你可以為不支援形狀的使用者設定一個外邊距,以此將內容推開;然後在特性查詢中將這個外邊距移除。

.content {
    margin-left: 120px;
}

@supports (shape-outside: circle()) {
    .content {
        margin-left: 0;
        /* add the rest of your shapes CSS here */
    }

}
複製程式碼
展開原文 With Firefox releasing their support we now only have one main browser without support for Shapes — Edge. If you want to see Shapes support across the board you could go and [vote for the feature here](https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/6263716-shapes), and see if we can encourage the implementation of the feature in Edge.

隨著 Firefox 釋出了對形狀的支援,現在剩下唯一還不支援形狀的主流瀏覽器就是 Edge 了。如果你希望看到形狀廣受支援,你可以前往這裡為這個特性投一票

更多關於 CSS 形狀的資訊

展開原文 In this article, I’ve tried to give a quick overview of some of the interesting things that are possible with CSS Shapes. For a more in-depth look at each feature, check out the [Guides to CSS Shapes](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Shapes) over at MDN. You can also read a [guide to the Shape Path Editor in Firefox](https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Edit_CSS_shapes).

在本文中,我試著對 CSS 形狀可能的用途做了一個概覽。關於每個特性更詳細的資訊,可以檢視 MDN 上的 Guides to CSS Shapes (CSS 形狀指導)。另外還可以檢視 guide to the Shape Path Editor in Firefox (Firefox 的 Shape Path Editor 使用指導) 。

相關文章