CSS Grid和自定義屬性帶來的變化
好久沒有整理有關於CSS方面的文章了,說實在心理還是癢癢的,但取捨有度。不過最近看了幾篇有關於CSS的文章還是蠻有意思的。兩篇是關於頁面佈局的,另外一篇是關於動畫函式的。事實上,佈局和動畫在CSS中都是較為重要的部分。當然,今天要提的知識點並不是什麼非常新的知識點,但也是有創意和創新的知識點。比如不通過媒體查詢實現響應式佈局,比如說容器單位構建強大的佈局,比如說動畫函式(緩動函式)的反轉。聽聽這些是不是覺得非常有意思,如果你和我也一樣,請繼續往下閱讀。
在繼續下面的內容之前,先說一下這幾個概念的出處:
- 不採用媒體查詢實現響應式佈局來自於 @Juan Martín García 的 博文
- 採用容器單位構建強大的佈局來自於 @Russell Bishop 的 博文
- 緩動函式的反轉來自於 @Michelle Barker 的 博文
不採用媒體查詢實現響應式佈局
首先要宣告一點,在這裡不會具體介紹什麼是媒體查詢,也不會說什麼是響應式設計。雖然如此,但大家或許和我類似,在心裡有一個概念,響應式設計會在不同的斷點有不同的響應(即不同的佈局效果),而這個不同的斷點就是依賴於媒體查詢來控制的。隨著Web佈局的發展,實現響應式佈局我們從此可以拋開媒體查詢來實現。正如 @Juan Martín García 的《 Look Ma, No Media Queries! Responsive Layouts Using CSS Grid 》文章中提到的相關技術。
在 中國第五屆CSS大會 (即將就要到來),知名CSS專家、Nexmo開發大使 @陳慧晶 老師的主題《新時代CSS佈局》或許會給我們帶來一些更新的佈局技巧和姿勢。就我自己而言,或許能猜到該主題要介紹的內容,但我還是非常期待。因為我一直有關注@陳慧晶 老師的文章和在國外分享的相關話題。
或許很多人會有疑問, 不使用媒體查詢怎麼來實現響應式佈局 。針對該問題,我以前也同樣沒有思考過,自從最近閱讀了@Juan Martin Garcia的文章,才恍然大悟,原來還可以這麼玩,同時也再次驗證了我的想法,未來的佈局是CSS Grid的天下。
在小站上有關於CSS的Grid佈局的教程也有不少,有關於相關的概念在相關的文章中也有相應的介紹。如果下文中提到的相關東西要是從未理解過,建議花點時間閱讀一下有關於 CSS Grid佈局相關的教程 。
正因為CSS Grid佈局有非常優秀的特性,比如 fr
單位 、 repeat()
函式 、 minmax()
函式 、 auto-fit
和 grid-auto-flow
。所以在沒有媒體查詢前提下,也同樣可以很輕易的實現響應式設計。甚至說可以比依賴媒體查詢實現響應式設計更為輕巧。
通過示例來向大家演示,怎麼藉助CSS Grid佈局來實現響應式設計。
你可以開啟上面的示例,嘗試著改變瀏覽器視窗的大小,你可以看到相應的變化:
在整個頁面中主要分為兩個部分,一個是全屏banner區域,另一個是卡片列表頁,隨著瀏覽器視窗大小變化,佈局也會相應的變化。就如上圖所示。
往往實現這樣的效果都是會藉助於媒體查詢來實處理,但上面這個示例,如果你查閱了程式碼,你會發現我們並沒有使用媒體查詢,而是使用CSS Grid佈局中的一些特性來處理的,關鍵程式碼如下:
/* 全屏banner區域*/ .hero { ... display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); align-items: center; } /* 卡片列表 */ .breweries > ul { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); grid-gap: 1rem; }
從上面的程式碼中不難發現,這裡最為關鍵的是在 grid-template-columns
中通過 repeat()
函式為網格列布局做為重複填充,其中之一是按 auto-fill
(自動填充)和 minmax()
來做相應的處理。有關於這幾個概念,這裡簡單的進行一下陳述:
-
repeat()
:表示網格列表的重複,可以讓我們以更緊湊的形式來處理網格列或行的佈局模式。它也接受兩個值:重複的次婁和重複的值 -
auto-fill
:給repeat()
函式使用這個關鍵詞,來替代重複次數。這可以根據每列的寬度靈活的改變網格的列數 -
minmax()
:能夠讓我們用最簡單的CSS控制網格軌道的大小;minmax()
函式接受兩個引數,一個最小值和一個最大值 -
fr
:代表網格容器中可用空間的一等份
是不是非常的簡單,只要你瞭解了CSS Grid佈局中相關的概念,可以讓我們很輕易的實現響應式的佈局。也就是說,我們今後可以在不依賴於媒體查詢的條件之下也可以實現響應式佈局,而且這種方式要更為簡單和輕巧。
有關於這方面更詳細的介紹,可以閱讀:
- Look Ma, No Media Queries! Responsive Layouts Using CSS Grid
- Responsive layout with CSS grid, part 2: auto-fill & auto-fit
- Grid is Perfect for Responsive Layout
採用容器單元構建強大的佈局
容器單元這個概念我也是第一次聽說,不得不佩服國外同行的創新和想法。簡單地說一下,容器單元是什麼?
容器單元是一組CSS自定義屬性的集合,允許我們使用列和間距構建的網格系統,通過構建出來的網格系統來實現頁面的佈局或元件的佈局。
簡單的說,藉助以前網格系統(此網格非彼網格)。在我們最早接觸的網格系統,比如說960gs網格,我們將一個容器等分成 12
或 24
列,列與列之間有一個間距,比如下圖這樣:
僅通過列寬,列間距和列數三個變數提供一組用於度量和計算的體系。
回過頭來思考一下,我們不難發現,大多數網格佈局都會依賴於它們們的父容器。而這裡提到的容器單元可以幫助我們如何使用CSS自定義屬性來克服這樣的一個限制,以及如何使用容器單元來構建佈局,而且是一個健壯的佈局。
如果你從未接觸過CSS的自定義屬性相關的知識,建議你花一點時間閱讀有關於這方面的相關知識。
接下來的內容,所設你對CSS自定義屬性有所瞭解了。那麼回到我們最為關心的問題:
- 如何建立一個容器單元
- 如何通過容器單元構建佈局
瞭解960gs網格系統的同學都應該知道,構建一個 12
列還是 24
列的網格系統,他們都有三個關鍵元素,即 容器寬度 、 網格列寬 和 列與列間距 ,如果我們用自定義屬性來表達的話,可以用下面的方式來描述:
:root { --grid-width: 960; --grid-column-width: 60; --grid-columns: 12; }
這三個值定義了 列的寬度 和 網格比例 (就上面的程式碼所示,網格的比例是 60/960
)。而 列間距 是從剩餘的空間中自動計算出來。
如果我們給容器寬度設定一個值,比如:
:root { --container-width: 84vw; }
如果藉助媒體查詢,我們可以在不同的斷點下設定不同的容器寬度,比如:
@media (min-width: 800px) { --container-width: 90vw; } @media (min-width: 1200px) { --container-width: 85vw; } @media (min-width: 1400px) { --container-width: 1200px; }
也就是說,我們可以將上面三個核心的概念分成三個不同的單位,也就是容器單元該具備的單元:
--column-unit --gutter-unit --column-and-gutterr-unit
結合起來就下面這樣,看起來有點複雜,只要你懂CSS自定義屬性,就不會覺得複雜:
:root { /* 網格屬性 */ --grid-width: 960; --grid-column-width: 60; --grid-columns: 12; /* 網格邏輯(列間距數量) */ --grid-gutters: calc(var(--grid-columns) - 1); /* 網格比例邏輯 列寬 / 網格寬度 */ --column-proportion: calc(var(--grid-column-width) / var(--grid-width)); --gutter-proportion: calc((1 - (var(--grid-columns) * var(--column-proportion))) / var(--grid-gutters)); /* 容器單元 */ --column-unit: calc(var(--column-proportion) * var(--container-width)); --gutter-unit: calc(var(--gutter-proportion) * var(--container-width)); --column-and-gutter-unit: calc(var(--column-unit) + var(--gutter-unit)); /* 容器寬度 */ --container-width: 80vw; } /* 媒體查詢改變容器寬度 */ @media (min-width: 1000px) { :root { --container-width: 90vw; } } @media (min-width: 1400px) { :root { --container-width: 1300px; } }
在使用網格的時候,我們有的時候會需要跨列合併,使用的時候可以像下面這樣:
.panel { width: calc(6 * var(--column-and-gutter-unit) - var(--gutter-unit)); }
有關於這方面更詳細的教程,可以閱讀 @Russell Bishop 的 Building Robust Layouts With Container Units 一文。
緩動函式的反轉
熟悉CSS中 animation
和 transition
的同學,都知道這兩個屬性中都有緩動函式的概念,即 animation-timin-function
和 transition-timing-function
對應的屬性值。這兩個屬性常見的屬性值主要有: linear
、 ease-in
、 ease-out
、 ease-in-out
等。除了這幾個還有貝塞爾曲線函式 cubic-bezier()
。
從上圖中可以看出, cubic-bezier()
函式主要由兩個點來控制,比如說點 (x1, y1)
和 (x2,y2)
,結合起來就是 cubic-bezier(x1,y1,x2,y2)
。而在 animation
中還有另一個屬性 animation-direction
可以讓動畫反轉( animation-direction: reverse
)。
為了反轉動畫的緩動曲線,我們需要在它的軸上旋轉180度,找到一個全新的座標。
如果緩動曲線的初始座標為 x1, y1, x2, y2
,那麼反轉之後的座標即為 (1-x2), (1-y2), (1-x1), (1-y1)
。既然知道了基本原理之後,我們同樣可以藉助CSS自定義屬性,用程式碼來表示:
:root { --x1: 0.45; --y1: 0.25; --x2: 0.6; --y2: 0.95; --originalCurve: cubic-bezier(var(--x1), var(--y1), var(--x2), var(--y2)); }
根據上面的公式,可以計算出反轉後的緩動曲線:
:root { --reversedCurve: cubic-bezier(calc(1 - var(--x2)), calc(1 - var(--y2)), calc(1 - var(--x1)), calc(1 - var(--y1))); }
為了更易於理解,把上面的程式碼稍作調整:
:root { /* 原始座標值 */ --x1: 0.45; --y1: 0.25; --x2: 0.6; --y2: 0.95; --originalCurve: cubic-bezier(var(--x1), var(--y1), var(--x2), var(--y2)); /* 反轉後的座標值 */ --x1-r: calc(1 - var(--x2)); --y1-r: calc(1 - var(--y2)); --x2-r: calc(1 - var(--x1)); --y2-r: calc(1 - var(--y1)); --reversedCurve: cubic-bezier(var(--x1-r), var(--y1-r), var(--x2-r), var(--y2-r)); }
最後來看一個 @Michelle Barker 的《 Reversing an Easing Curve 》文中提供的一個示例:
小結
雖然文章中提到的知識點非常的有意思,不管是使用CSS Grid實現響應式設計,還是容器單元構建頁面佈局,或者說緩動曲線的反轉。看上去有點高階和複雜,但簡單地說,都是基於一些基礎知識來構建的,比如CSS的Grid佈局和CSS自定義屬性。其實這兩個知識點都不是很新的概念,但把這些東西結合起來,可以幫助我們達到更為神奇和強大的功能。如果你還未接觸過這方面的知識,那麼你需要開始去學習了,在未來的CSS中,這兩個東東都是非常重要的知識點,也是非常有用的知識的。在不久的將來,這兩個部分都可以運用於你的專案中,我在去年的專案中就已經嘗試使用了CSS的自定義屬性,基本上能達到預期的效果。讓我更易於維護和修改專案的需求。如果你在這方面有更多的經驗,歡迎在下面的評論中與我們一起分享。