詳解16個CSS新特性(2021最新版-上)

 2021 CSS 的新特性和以前兩年的相比有類似也有不一樣,本文就帶你們看看今年的 CSS 到底說了什麼。javascript



簡介php


若是您有關注過這兩年的 CSS 發展狀態報告(2019年和2020年)的話,不難發現,在報告中有專門關於 CSS 新特性一項的介紹。
css



圖片by 2020 年 CSS 發展狀態報 告(https://2020.stateofcss.com/en-US/features/)


是否是有種似有相識的感受,或者有一些又是那麼的陌生。

對於我而言,這些都不陌生。不過我想說的是上圖(或者報告)中提到的只是冰山一角,有不少是沒有出如今調查問卷和報告中。

好比接下來內容中提到的僞類選擇器,內容可見性,容器查詢等等。

嗯!先從 CSS 選擇器開始!(^_^)


CSS僞類選擇器html


CSS 選擇發展到今日,能夠說是一個龐大的體系了:前端


圖片by  @linxz 的博客java


如今描述 CSS 選擇器的規範主要有 CSS (指的是 CSS 2.1),後面分爲選擇器 Level 3 和 Level 4。僞類選擇器在這三部分規範中都有出現過,他們是一個遞進的過程,特別是在 Level 3 和 Level 4,在原有的基礎上新增了很多優秀的選擇器。今天要提到的幾個現代僞類選擇器就是出於 Levele 4:ios



  :is() 和 :where()


先來看 :is() 和 :where() 。Elad Shechter 曾在推特上發了一個關於 :is() 和 :where() 選擇器的測試題:


若是你是第一次看到這樣的測試題,請先自測一下,若是是你,你會選擇哪一個答案(greenpurplered 仍是 blue)?

若是你選擇的是 purple ,那麼要恭喜你,你答對了。 

Demo 地址:https://codepen.io/airen/full/ExWxbKe

示例中出現了 :is() 和 :where() 兩個可能你從未接觸過的僞類選擇器:

:is(.header, .main) .p { color: purple}:where(.header, .main) .p { color: red;}

其實這兩個選擇器等同於:

.header .p,.main .p { color: purple;}.header .p,.main .p { color: red;}

他們惟一不一樣之處,就是選擇器權重不一樣。:where() 的優先級老是爲 0 ,可是 :is() 的優先級是由它的選擇器列表中優先級最高的選擇器決定的。那麼上面的示例中,咱們能夠用下圖來清晰的描述他們之間的關係:

圖片by   twitter

即,下面三個選擇器選中的相同的元素:

.header .p,.main .p { // ...}
:is(.header, .main) .p { // ...}
:where(.header, .main) .p { // ...}

不一樣的是他們的權重不一樣,其中:

.header .p,.main .p { // ....}
:is(.header, .main) .p { // ...}

示例中的兩種選擇器具備相同的權重(即020);其中:

:where(.header, .main) .p { // ...}
.p { // ...}

上面示例代碼中的選擇器具備相同權重(即 010):


:is() 和 :where() 僞類選擇器的出現,將會讓咱們的選擇器變得更簡潔,好比下成這個 :is() 的示例:

// Level 0h1 { font-size: 30px;}// Level 1 section h1, article h1, aside h1, nav h1 { font-size: 25px;}// Level 2 section section h1, section article h1, section aside h1, section nav h1,article section h1, article article h1, article aside h1, article nav h1,aside section h1, aside article h1, aside aside h1, aside nav h1,nav section h1, nav article h1, nav aside h1, nav nav h1, { font-size: 20px;}

使用 :is() 能夠像下面這樣來描述:

// Level 0h1 { font-size: 30px;}// Level 1:is(section, article, aside, nav) h1 { font-size: 25px;}// Level 2 :is(section, article, aside, nav):is(section, article, aside, nav) h1 { font-size: 20px;}

回過頭來,請你們再看看下面這個測試題,請問 p 文本的顏色是什麼?

<!-- HTML --><main class="main"> <p class="p">What is the text color?</p></main>// CSS:where(.header, .main) .p { color: red;}.p { color: blue;}:is(.header, .main) .p { color: purple;}.header .p,.main .p { color: green;}
查看答案:https://codepen.io/airen/full/dyvyJmW


 :not() 和 :has()


或許你們平時在開發前端頁面的時候,碰到相似下圖這樣的需求:web



但願最後一張卡片沒有margin-bottom (或第一張卡片沒有 margin-top)。

針對於這樣的場景,:not() 選擇器就很是有優點。爲何這麼說呢?

在沒有 :not() 選擇器的時候,你可能會想到下面這樣的方式:

.card + .card { margin-top: 20px;} 
// 或
.card { margin-bottom: 20px;}
.card.card--last { // 也可能會使用 .card:last-child margin-bottom: 0;}

若是換成 :not() 選擇器,能夠這要來實現:

.card:not(:last-child) { margin-bottom: 20px}

效果以下:

Demo URL: https://codepen.io/airen/full/MPNLEo

雖然 CSS 選擇器已經很是強大了,但一直以來,在 CSS 中沒有從子元素選到父元素的樣的選擇器(父選擇器):

有人提出但願有一個 :parents() 這樣的選擇器!typescript


雖然直到目前爲止尚未 :parents() 選擇器,但慶幸的是,:has() 選擇器即將到來,它能夠用來選擇父級元素。

目前 Igalia 公司正在爲 Chrome 實現該選擇器,其團隊成員 Brian Kardell 新發表的《Can I :has()》文章中對 :has() 選擇器進行了詳細的闡述。


<section><!-- section 邊框顏色是 blue,margin-bottom是 30px -->  <h1>H1 Level Title</h1></section> 
<section><!-- section 邊框顏色是 #09f,margin-bottom是 30px --> <h1>H2 Level Title</h1></section>
<section><!-- section 邊框顏色是 red --> <p>Text Paragraphs</p></section> /* CSS */
// 將匹配含有h1子元素的 section元素section:has(h1) { border-color: blue;}// 將匹配含有h2子元素的 section元素section:has(h2) { border-color: #09f;}// 將匹配含有p子元素的 section元素section:has(p) { border-color: red;}// 將匹配除了含有p子元素的 section元素section:not(:has(p)) { margin-bottom: 30px;}


在支持:has()的瀏覽器中,你將看到下圖這樣的效果:canvas



示例中還演示了 :not():has() 組合在一塊兒使用的,但二者組合在一塊兒所達到的意思卻徹底不同。


其中 :not(:has(selector)) 匹配不含有 selector 元素的父元素,而 :has(:not(selector)) 匹配含有的不是 selector 子元素的元素。


二者主要區別在於,:has(:not(selector)) 寫法必需要含有一個子元素,而 :not(:has()) 能夠不含有元素也會被匹配。


有意思的是,Github 中有一個 Issue 在討論** **:has-child()** 選擇器**,或許哪一天,咱們在組件中就能夠這樣使用:



  :empty 和 :blank


在現代 Web 的開發的過程當中,老是沒法避免數據吐出爲空的狀況,此時每每會給咱們的 UI 帶來額外的麻煩。好比說,在同一類的 UI 元件中編寫了必定的樣式規則,但數據爲空,此時在頁面上可能會出現這樣的場景:

CSS 的 :empty 和 :blank兩個僞類選擇器能夠幫助咱們避免這種現象。這兩個選擇器都頗有用:


  • 給空元素添加樣式

  • 建立空的狀態


既然都是能夠爲空元素添加樣式,那何爲空元素,他們之間的差別又是什麼?先來回答第一個問題,何爲空元素?空元素是指元素沒有任何子元素或子節點,好比:


<!-- 空元素 --><div class="error"></div><div class="error"><!-- 註釋 --></div>
<!-- 非空元素 --><div class="error"> </div><!-- 中間有一個空格符 --><div class="error"></div><!-- 斷行 --><div class="error"> <!-- 註釋 --></div><!-- 註釋斷行排列 --><div class="error"><span></span></div>


:empty 和 :blank 相比,:empty 只能選中沒有子元素的元素。子元素只能夠是元素節點或文本(包括空格)。註釋或處理指令都不會產生影響。


Demo: https://codepen.io/airen/full/yLMLKyo


注意,在空元素上即便使用僞元素 ::before ::after 建立內容,也能被:empty 識別。


:blank 要比 :empty 靈活地多,只要該元素中無任何子元素都能被識別。


不過 W3C 規範對該僞類選擇器的定義更趨向於做用到表單控件中,好比用戶沒有在 inputtextarea 中輸入內容時,提交表單能被識別到。有點相似於表單驗證的功能。


早在 2018 年 Zell Liew 在推特上就針對 :empty:blank 作過相關的討論,並發表一了篇有關於這方面的博文《:empty and :blank》:



到目前爲止,:empty 已獲得主流瀏覽器支持,能夠用於實際生產中,但 :blank 僞類選擇器仍是存在必定的爭議的。


  :focus-visible 和 :focus-within


CSSer 對於 :focus 應該不會感到陌生,早期對於可聚焦元素可使用 :focus 來給元素設置焦點狀態下的 UI 風格,即焦點環樣式:


Chrome 86 開始,另外引入了 :focus-visible 和 :focus-within 兩種僞類選擇器來幫助咱們更好的控制 UI 焦點環的樣式:



雖然 :focus :focus-within:focus-visible 均可以用來設置焦點環的樣式,但他們之間仍是有必定的差別:

  • :focus :當用戶使用鼠標點擊焦點元素或使用鍵盤的 Tab 鍵(或快捷鍵)觸發焦點元素焦點環的樣式
  • :focus-visible :只有使用鍵盤的 Tab 鍵(或快捷鍵)觸發焦點元素焦點環的樣式。若是僅使用 :focus-visible 設置焦點環樣式的話,那麼用戶使用鼠標點擊焦點元素時不會觸發焦點環樣式
  • :focus-within:表示一個元素得到焦點,或該元素的後代元素得到焦點。這也意味着,它或它的後代得到焦點,均可以觸發 :focus-within

來簡單的看一個示例:


button:focus {  outline: 2px dotted #09f;  outline-offset: 2px; } 
button:focus-visible { outline: 2px solid #f36; outline-offset: 2px; }


你會發現,分別使用鼠標點擊按鈕和按Tab 讓按鈕得到焦點時焦點環樣式效果不一樣: 



不過須要注意的是,:focus 和 :focus-visible 也會涉及到選擇器權重的問題,就上面的示例來講,若是咱們把 :focus 選擇器對應的樣式放置到 :focus-visible 以後:


button:focus-visible {  outline: 2px solid #f36;  outline-offset: 2px; }
button:focus { outline: 2px dotted #09f; outline-offset: 2px; }


這個時候,你會發現無論用戶使用鍵盤 Tab 鍵仍是鼠標讓 <button> 得到焦點時,焦點樣式都會採用 :focus 對應的樣式:



若是咱們要讓 :focus 和 :focus-visible 能夠有獨自的樣式,能夠藉助CSS選擇器中的 :not() 來處理:


button:focus:not(:focus-visible) { outline: 2px dotted #416dea; outline-offset: 2px; box-shadow: 0px 1px 1px #416dea;}
button:focus-visible { outline: 2px solid #416dea; outline-offset: 2px; box-shadow: 0px 1px 1px #416dea;}


這個時候按 Tab 鍵盤和鼠標點擊時焦點環樣式就有相應的差別:


Demo: https://codepen.io/airen/full/YzGdoLq


或許你想到這樣的場景了,若是你在作 A11Y 方面的優化,但願在移動端和PC端上對焦點元素設置不一樣的焦點環樣式,那用上面這種方案來講就會很是的靈活。

對於 :focus-within 而言,它其實有點相似於 :has() 僞類選擇器,能夠在子元素獲得焦點時,觸發父元素:


上圖描述了 :focus-within 和 :focus 之間的差別。更簡單的說,它有點相似於 JavaScript 的事件冒泡,從可得到焦點元素開始一直冒泡到HTML的根元素 <html> ,均可以接收觸發 :focus-within 事件。好比:


.box:focus-within,.container:focus-within { box-shadow: 0 0 5px 3px rgb(255 255 255 / 0.65);}
body:focus-within { background-color: #2a0f5bde;}
html:focus-within { border: 5px solid #09f;}複製代碼


Demo: https://codepen.io/airen/full/JjRxoLE


藉助於 :focus-within 僞類選擇器的特性,咱們就可讓一些交互效果變得更爲簡單,好比下面這個示例,使用 :focus-within 就能夠實現,再也不須要任何 JavaScript 代碼:

Demo: https://codepen.io/airen/full/KrPLmV


有了 :focus:focus-visible :focus-within 會讓咱們更好的管理焦點元素在焦點狀態下焦點環的 UI 效果。

在 CSS 的世界中,除了這裏提到的僞類選擇器以外,還有不少其餘的僞類選擇器(或僞元素),好比 ::marker:in-range:out-of-range



若是你感興趣的話,能夠嘗試着使用 :in-range:out-of-range 給一些 input (好比 type numberrange input 元素,他們都有 min max 屬性的設置) 在用戶輸入範圍值和非範圍值時,提供不一樣的 UI 效果。


CSS 顏色


CSS 顏色模塊自從 Level 4 開始,除了新增了一些新的函數值,好比 hwb()lch()lab()color-mix()color-contrast()color() 以外,對本來的 rgb()hsl() 以及 #rrggbb 等顏色值定義語法規則也作出調整。


好比,本來咱們熟悉的描述顏色值的方式:


#09f #90eafergb(123, 123, 123)rgba(123, 123, 123, .5)hsl(220, 50%, 50%)hsla(220, 50%, 50%, .5)


都有了新的語法規則,特別是對於 rgb()rgba()hsl() hsla() ,以往用逗號(,)做爲分割符,如今能夠直接使用空格作分割符,而且rgb()hsl() 函數在第三個值和第四個值之間加上 / 能夠取替 rgba()hsla()



另外,十六進制描述顏色,也能夠在本來的語法規則中最後兩位添加新的位值來描述透度明。好比 #rrggbbaa 或 #rgba 。在Chrome 瀏覽器代碼審查器中,已經能夠看到這種語法規則的身影了: 



也就是說,咱們如今能夠這樣來描述顏色值:


#hex-with-alpha { color: #0f08; color: #00ff0088;}
#functional-notation { color: hsl(0deg 0% 0%); color: hsl(2rad 50% 50% / 80%); color: rgb(0 0 0); color: rgb(255 255 255 / .25);}
#lab-and-lch { --ux-gray: lch(50% 0 0); --rad-pink: lch(50% 200 230); --rad-pink: lab(150% 160 0); --pale-purple: lab(75% 50 -50);}


另外,咱們如今描述顏色都是在sRBG 色值空間,而顏色色值空間是一個複雜的體系,除了 sRGB 以外還有其餘的色值空間,好比說 LCH



正如上圖所示,LCH 顏色空間的顏色數量要比 sRGB 顏色空間的多,並且描述的顏色更爲細膩。



如上圖所示,能夠在color() 函數中指定顏色空間:


#color-function { --rad-pink: color(display-p3 1 0 1); --rad-pink: color(lab 50% 150 -50); --rad-pink: color(srgb 100% 0% 50%);}


注意,使用了color() 函數指定顏色空間除了要考慮該函數支持度(瀏覽器的兼容性)還須要考慮硬件設備對顏色空間的支持度。

在 CSS 中能夠藉助媒體查詢 @media 來作相應的判斷,好比下面的示例,若是終端設備支持的話,就會採用指定顏色空間的色值:


@media (dynamic-range: high) { .neon-pink { --neon-glow: color(display-p3 1 0 1); }
.neon-green { --neon-grow: color(display-p3 0 1 0); }}


更爲強大的是 CSS 顏色模塊 Level 5 版本,對顏色函數能力作了進一步的擴展。好比,能夠在 rgb()hsl() 、hwb() 、lab() 和 lch() 函數基礎上添加 from 關鍵詞,實現基於一個顏色的基礎上,只對某個參數作調整:


rgb() = rgb([from <color>]? <percentage>{3} [ / <alpha-value> ]? ) | rgb([from <color>]? <number>{3} [ / <alpha-value> ]? ) hsl() = hsl([from <color>]? <hue> <percentage> <percentage> [ / <alpha-value> ]? ) hwb() = hwb([from <color>]? <hue> <percentage> <percentage> [ / <alpha-value> ]? ) lab() = lab([from <color>]? <percentage> <number> <number> [ / <alpha-value> ]? ) lch() = lch([from <color>]? <percentage> <number> <hue> [ / <alpha-value> ]? )複製代碼


來看一個 hsl() 的示例:



上圖展現的是,基於 --theme-primary (原色,即 hsl(274, 61%, 50%))顏色只對l 參數作調整,從 50% 調整到 30% ,從而得到一個新的顏色,即 hsl(274, 61%, 30%) 。使用這樣的方式,咱們就能夠很輕易的獲取基於某個顏色參數改變得來的顏色面板:



除此以外, 顏色模塊 Level 5 版本還新增了一些新的函數用來描述顏色,好比 color-mix() 、color-contrast() 、color-adjust() 等:


.color-mix { --pink: color-mix(red, white);
--brand: #0af; --text1: color-mix(var(--brand) 25%, black); --text2: color-mix(var(--brand) 40%, black);}
.color-contrast { color: color-contrast( var(--bg) vs black, white );}
--text-on-bg: color-contrast( var(--bg-blue-1) vs var(--text-subdued), var(--text-light), var(--text-lightest));
.color-adjust { --brand: #0af; --darker: color-adjust(var(--brand) lightness -50%); --lighter: color-adjust(var(--brand) lightness +50%);}



簡單介紹一下這幾個顏色函數。其中 color-mix() 函數接受兩個 <color> 值,並在給定的顏色空間中以指定的數量返回它們混合的結果。若是沒有特殊說明,不然在 lch() 顏色空間中進行混合。好比下圖所展現的就是 在lch() 顏色空間(默認)中將 redyellow 混合,每一個 lch 通道的紅色值佔65%,黃色值佔 35%,即 color-mix(red yellow 65%)



color-contrast() 函數頗有意思,它能夠幫助咱們提升Web可訪問性方面的能力。其主要做用是獲取一個顏色值,並將其與其餘顏色值的列表進行比較,從列表中選擇對比度最高的一個。



好比 color-contrast(white vs red, white, green) ,分別會拿 redwhitegreenwhite 對比,其中 greenwhite 對比度最高,最終會取 green 顏色:



color-adjust() 函數可用來在給定顏色空間中經過指定的變換函數對該顏色進行調整,除非另有規定,不然調整是在 lch() 色彩空間中進行的。好比 color-adjust(#0af lightness +50%) 是顏色 #0aflch 顏色空間中高度增長 50%


也就是說,在不久的將來,這些顏色函數能夠很好的幫助咱們控制Web中的顏色值。由於這些顏色函數都已成爲主流瀏覽器的實驗性屬性,特別是 Safari 瀏覽器,對這方面的支持度要高於 Chrome 和 Firefox 瀏覽器。


CSS 背景


CSS 的 background 屬性是一個簡寫屬性,這個對於你們來講再熟悉不過了。不過在這裏着重想把 background-positionbackground-repeat 單獨拿出來和你們聊聊。你可能會問,這兩個屬性又不是什麼新屬性了,有什麼值得聊的呢?其實未必,這兩個屬性雖然老,但有幾個小細節對於不少開發者來講仍是會感到陌生的。我們先從 backgroundd-position 開始。


  background-position


background-position 主要是用來指定背景圖片在容器中的位置。你們較爲熟悉的是,背景圖片左上角(頂點)相對於容器左上角計算,以下圖所示:



不過,有的時候,但願背景圖片能相對於容器右側邊緣或底部邊緣計算,好比下圖:



要讓背景圖片距離容器右側邊緣和底部邊緣都是 50px 。針對這樣的場景,你或許首先會想到使用容器大小和背景圖片大小進行計算,得出距離頂部和左側邊緣的距離,而後將計算出來的值運用於 background-position 中。固然,熟悉 CSS 的同窗或許會想到使用 calc() 函數:


:root { --xPosition: 50px; --yPosition: 50px;}
.container { background-position: calc(100% - var(--xPosition) calc(100% - var(--yPosition)))}


使用 calc() 動態計算要比經過容器和背景圖片尺寸大小計算方便得多。


事實上呢?background-position 提供了另外一種特性,咱們能夠經過關鍵詞 top 、right 、bottom 和 left 來指定方向。好比咱們熟悉的用法 :


background-position: 50px 50px;
// 至關於background-position: top 50px  left 50px;


那麼,咱們要實現上圖的效果,就可使用 right 和 bottom 關鍵詞,讓事情變得很是簡單:


:root { --xPosition: 50px; --yPosition: 50px;}
.container { background-position: right var(--xPosition) bottom var(--yPosition);}


最終效果以下:


Demo: codepen.io/airen/full/…


注,示例中左側是使用 calc() 計算的效果,右側使用的是關鍵詞實現的效果。


background-position 使用還有一個小細節須要注意,即 background-position 採用百分比值。由於使用百分比值的 background-position 計算會相對於其餘單位值複雜:



正如上圖所示,背景圖片原始尺寸是 100px x 100px ,容器的尺寸(使用該背景圖片的元素)是 410px x 210px ,若是使用 background-position: 75% 50% 時,它的計算以下:


// background-position的x軸座標x = (容器寬度 - 背景圖片寬度) x background-position的x座標的百分比值 = (410 - 100) x 75% = 232.5px
// background-position的y軸座標y = (容器高度 - 背景圖片高度) x background-position的y座標的百分比值 = (210 - 100) x 50% = 55px


就該示例而言,background-position: 75% 50% 就至關於 bacckground-position: 232.5px 55px


注意,若是咱們把 background-sizebackground-position 取百分比值場景結合起來使用的時候,會讓事情變得更爲複雜,特別是background-size取值爲covercontain 更會讓你感到蛋疼。爲何會這樣呢?這已經超出來這篇文章所要探討的話題,若是你感興趣的話,能夠以此爲課題,深刻探討一下。


  background-repeat


在 background-repeat 屬性上除了可使用咱們熟悉的 no-repeat 和 repeat (或者 repeat-x 和 repeat-y) 以外還可使用 round 和 space 

咱們都知道,使用 repeat 的時候,有可能會形成背景圖片在平鋪的時候被裁剪。若是但願背景圖片在平鋪時不被剪切,那麼可使用 round 來替代,它的最大特點就是背景圖片在平鋪的時候會根據容器的寬高對背景圖片作相應的尺寸調整。而 space 會留出相應的空間,即在保證背景圖片不被裁剪的狀況之下,對多出來的空間以空白的方式在背景圖片之間留出。



針對於這些值,我錄製了一個簡單的視頻,從視頻的展現效果中能夠更好的看出它們之間的差別:


Demo: https://codepen.io/airen/full/mdWyOOj


CSS 蒙層和剪切


若是你對設計或設計軟件較爲熟悉的話,對於蒙層和剪切不會感到陌生。設計師在作一些設計稿的時候,時常也會用到蒙層和剪切的能力。隨着 CSS 的發展,在 CSS 的世界中也有了這兩個特性,它們在 W3C 的 《CSS Masking Module Level 1》規範中定義,主要的做用以下圖所示:



能夠靈活的控制內容的顯示區域。

蒙層和剪切對應的 CSS 屬性就是 maskclip-path ,其中 mask 是一個簡寫屬性,它的使用規則和 background 很是的類似。我將經過簡單的示例來向你們展現它們能幫咱們作些什麼。

先看 mask

Demo: https://codepen.io/airen/full/MdQrvR


視頻中的 emoji 和文本殘缺不全(看上去被啃了同樣)。在沒有mask 的能力以前,若是咱們要實現這樣的效果幾乎是不太可能,如今有了以後,實現起來就很是的簡單。咱們只須要像下面這樣的一張圖片(用於mask-image 上的圖片),即蒙層圖:



h1 { mask-image: url(mask.png);}複製代碼

並且咱們還能夠藉助 
mask  的合成能力,讓多個蒙層作合成運算:



運用蒙層相關的能力,能夠快速幫助咱們實現一些業務場景所需的效果:



除此以外還能夠實現換膚的效果:


Demo: https://codepen.io/airen/full/yWvzYy


再來看剪切。在 CSS 中使用 clip-path 的能力,除了能夠幫助咱們控制好所要展現的區域以外,還能夠結合不一樣的路徑和函數值實現不規則的圖形展現。好比 Clippy 所展現的效果:


好比在實際需求中,須要實現一些不規則,又是多狀態下的 UI 效果,那麼 clip-path 就會很方便:


並且使用clip-path 結合 transition 或 animation 能夠在交互上作一些更好的動效:

左側是原狀態下效果,右側是鼠標懸浮狀態下效果:

Demo: https://codepen.io/airen/full/zYqbgRK


特別是,當clip-path 支持 path() 函數時,能夠作得事情更多了,好比咱們想要繪製一個心形的 UI 效果:



另外就是把 clip-path 、float 和 CSS 的 Shapes 結合能夠實現一些不規則的圖文混排的佈局效果:



CSS 混合模式


CSS 混合模式是個頗有意思的特性,目前主要有 mix-blend-modebackground-blend-mode 兩個屬性,前者是用於多個元素的合成,後者是用於多個背景的合成。使用它們能夠實現一些特殊的效果,好比相似 Photoshop 中的濾鏡效果:




採用混合模式特性,咱們能夠輕易的實現產品圖換色的效果:


Demo: https://codepen.io/kylewetton/full/OJLmJoV


這裏簡單地介紹一下,這個效果是怎麼實現的。


首先咱們有一張相似下圖的產品圖:



經過 SVG 的能力,咱們描繪圖一個純黑色的 SVG 形狀,形狀和上圖的沙發是相吻合的:



描繪出來的 SVG 代碼並不複雜:


<svg id="js-couch" class="couch__overlay" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none" width="1000" height="394">  <defs>  <path d="M996.35 77.55q-1.85-1.95-8.65-3.75l-62.4-17.1q-9.3-2.75-12.15-2.5-1.8.15-2.85.45l-.75.3q2.25-16.3 3.75-22.05 1.15-4.4 1.4-10.8.2-6.6-.7-10.85-1.25-5.65-3.1-7.8-2.95-3.35-9.65-2.7-5.95.6-39.3 1.7-38.3 1.25-39.45 1.3-10.25.5-126.75.5-5.05 0-54.2 1.3-45.8 1.25-54.05.95-19.45-.45-30.4-.7-20.2-.55-23.1-1.3-22.3-5.85-26.5 1.25-2.65 4.55-3.85 7.9-.6 1.7-.7 2.5-.65-2.2-2.05-4.55-2.75-4.65-6.45-5.2-3.85-.55-13.65-.4-7.4.1-12 .4-.4.05-18.7.9-16.55.8-19.15 1.1-3.4.4-14.6 1.1-11.3.75-13.05.65h-9.8q-8.65-.05-11.45-.4-2.85-.35-9.25-.6-6.7-.15-8.5-.25-2.7-.1-27.75-.1-25.1 0-29.6.1-92.35 1.15-99 1.65-5.15.4-20 0-15.3-.4-24.4-1.25-6.75-.6-21-1.55-12.95-.9-14.85-1.1-6.45-1.05-11.05-1.5-8.7-.85-12.85.5-5.45 1.75-8.1 4.65-3.2 3.4-2.9 8.6.25 4.65 2.1 11.8 1 3.8 2.55 9.1 1 3.85 2.35 10.1-.1 1-1.5 1-1.75 0-7.7.85-7.1 1-9.8 2.05-2.4.9-23 4.75-21.2 3.9-22.05 4.15-8.2 1.85-15.05 3.35Q7.4 69.1 5.65 70.3 2.5 72.45 2 73.1.6 75 .75 79.2q.15 4.15 1.3 12.75.9 6.85 1.45 10 .5 2.75 8.55 54 6.65 42.15 7.35 46.85 1.15 7.65 4.9 28.55 4.55 25.2 6.35 31.2 2.45 8.15 3.8 11.75 1.85 4.9 3.2 5.75 1.25.8 6.85.65 2.75-.05 5.3-.25l23.85.35q.1 0 1 .95t2 .95q1.9 0 3.4-1.4l23.1-.25 43.65.4q135.05 2.15 137.9 1.9 1.25-.1 72.9.5 72.45.65 76.85.45 8.1-.35 64 .4 143.35.95 146 1.1.55.05 75.3.3 74.7.3 79.8.6 8.65.5 68.25-.35l51.75.5 1.6.4q1.95.35 3.8.05 1.45-.25 3.5-.2 1.9 0 3.35-.3 2.1-.45 8.25-.8 6.25-.3 8.75-.05 1.7.2 8 1 5.75.3 7.4-1.75 1.75-2.2 4.95-10.85 2.8-7.55 4.05-12.4.65-2.5 3.6-17.2 2.75-13.75 3.15-14.8.45-1.25 4.45-22.85 4.05-22.4 4.4-24.4.3-1.45 3.75-25.2 3.35-23.2 4-26.3 1.15-5.5 2.35-18.8 1.4-15.7.8-23.7-.6-8.35-3.35-11.15z" id="a" />  </defs>  <use xlink:href="#a"/> </svg>


使用 CSS 自定義屬性,配合少量的 JavaScript代碼便可完成產品圖換色的效果: 


:root { --fill: #f36;}
svg { fill: var(--fill); mix-blend-mode: multiply;}


咱們使用 mix-blend-mode 的 multiply 效果。另外,JavaScript 動態改變 --fill 的值代碼也簡單:


const inputEle = document.querySelector('input') 
inputEle.addEventListener('change', (e)=>{ document.documentElement.style.setProperty('--fill', e.target.value); })複製代碼


很簡單吧,並且你能夠發揮你的想象空間,能夠實現不少具備創意的效果。


CSS 自定義屬性


CSS 自定義屬性能夠說是 CSS 的標配了,正如 W3C 規範所描述的,CSS 自定義屬性又被稱爲 CSS 變量:



不過在這裏要說的不是 CSS 自定義如何使用?要和你們說的是 CSS 自定義屬性中的無效變量。CSS 自定義屬性中的無效變量是頗有用的一個特性,它能夠實現 1 (真)和 0 (假)的開關切換效果。換句話說,能夠很好的實現不一樣狀態的 UI 效果。


一樣地,在 W3C 規範中對無效變量有詳細描述,但不仔細的話,仍是會忽略該特性。先來看規範是怎麼描述無效變量的:



它的意思是:

當一個自定義屬性的值是 **initial**時,**var()**** 函數不能使用它進行替換。除非 指定了一個有效的回退值,不然會使聲明在計算值時無效。**


在 CSS 中註冊自定義屬性時是使用 -- 來註冊的,能夠給已註冊的自定義屬性賦值,包括空字符串,但其中有一個細節很是有意思:


:root { --invalid:; // 注意冒號和分號之間無空格符,也無任何字符 --valid: ; // 注意冒號和分號之間只有一個空格符}


其中,--invalid 自定義屬性被稱爲無效變量,而 --valid 自定義屬性是一個有效變量。使用上面這種方式來區分有效和無效變量對於開發者而言,可讀性極差,並且有些文本編輯器可能會對代碼按本身配置的規格格式化,有可能會形成 --invalid:; 變成 --invalid: ; (有空格)。爲此,通常使用關鍵詞 initial來顯式聲明一個自定義屬性爲無效變量:


:root { --invalid: initial; // 無效變量 --valid: ; // 有效變量}


若是不使用 var() 函數調用已註冊的自定義屬性的話,那麼對於已註冊的自定義屬性而言,不會起任何做用。而 var() 函數有兩個參數,第一個參數就是自定義屬性,第二個參數是備用值。當第一個參數是個無效值時,會採用第二個參數。正因如此,對於已註冊的無效自定義屬性(即無效變量),好比 上面代碼中的 --invalid 。那麼 var() 沒有提供備用值(第二個參數),則會使 CSS 樣式規則(聲明)在計算值時無效:


:root { --invalid: initial; // 無效變量 --valid: ; // 有效變量}
foo { background-color: var(--invalid); // 未提供備用值,則background-color 計算值無效 color: var(--invalid, red) // 提供了備用值,--invalid是無效變量,則會採用備用值 red}


先來看一個簡單的示例:


<div class="element"> <i>Element</i></div>
<i>Element</i>
<style> .element { --color: red; // 只做用於 .element 元素及其後代元素 }
i { --foo: initial; // 無效變量 --color: var(--foo); // 無效變量 background-color: var(--color, orange); // --color是無效變量,採用備用值 orange }</style>


運行上面的代碼,你將看到的效果以下圖所示: 


Demo: https://codepen.io/airen/full/GRWJGvx


再來看另外一個示例:


<section>Element</section>
<style> :root { --initial: initial; // 無效變量 --valid: ; // 有效變量 } section { background-color: var(--initial, red); // 無效變量,會採用備用值 red color: var(--valid, red); // 有效變量,備用值被忽略 }</style>


所以,你看到的效果以下:


Demo: https://codepen.io/airen/full/poeJKpM


這個示例中 color 屬性至關於設置了 color: ; 值,客戶端在計算該值時將被忽略,所以會繼承其祖先元素的 color 值,該例繼承了 body 元素的 color black
你可能對 CSS 自定義屬性中的無效變量有了必定的認知。咱們繼續往下。
爲了在使用 CSS 自定義屬性中的無效變量讓開發者更易於理解,@Lea Verou在《The --var: ; hack to toggle multiple values with one custom property》引入了相似於Switch的概念,即:



:root { --ON: initial; // 無效變量,至關於開啓 var()的備用值 --OFF:; // 有效變量,至關於關閉 var()的備用值}


這樣一來,咱們在作 UI 不一樣狀態切換時,就只須要對 --ON 和 --OFF 的切換。好比某篇文章中提供的示例:


:root { --ON: initial; --OFF: ;}
button { --is-raised: var(--OFF); border: 1px solid var(--is-raised, rgb(0 0 0 / 0.1)); background: var( --is-raised, linear-gradient(hsl(0 0% 100% / 0.3), transparent) ) hsl(200 100% 50%); box-shadow: var( --is-raised, 0 1px hsl(0 0% 100% / 0.8) inset, 0 0.1em 0.1em -0.1em rgb(0 0 0 / 0.2) ); text-shadow: var(--is-raised, 0 -1px 1px rgb(0 0 0 / 0.3));}
button:hover { --is-raised: var(--ON);}
button:active { box-shadow: var(--is-raised, 0 1px 0.2em black inset);}


最終效果以下:


Demo: https://codepen.io/airen/full/XWNYRga


上面這個示例算是簡單的了。不過咱們能夠利用該特性實現更復雜的 UI 效果。好比下面這個多種換膚效果:


label { --box-shadow: var(--ON); --box-shadow-active: var(--OFF); box-shadow: 0 0 0 3px var(--box-shadow, rgba(0, 0, 0, 0.05)) va r(--box-shadow-active, #2196f3); cursor: pointer;}
label.dark { background-color: var(--dark-bgcolor);}
label.light { background-color: var(--light-bgcolor);}
label.blue { background-color: var(--blue-bgcolor);}
#dark:checked ~ div .dark,#light:checked ~ div .light,#blue:checked ~ div .blue { --box-shadow: var(--OFF); --box-shadow-active: var(--ON);}
.nav { color: var(--light, var(--light-color)) var(--dark, var(--dark-color)) var(--blue, var(--blue-color)); background-color: var(--light, var(--light-bgcolor)) var(--dark, var(--dark-bgcolor)) var(--blue, var(--blue-bgcolor));}
a.active,a:hover { background-color: var(--light, var(--light-active-bgcolor)) var(--dark, var(--dark-active-bgcolor)) var(--blue, var(--blue-active-bgcolor));}
/* 設置切換開關 */:root { --ON: initial; --OFF: ;
/* Dark */ --dark-color: rgba(156, 163, 175, 1); --dark-bgcolor: rgba(17, 24, 39, 1); --dark-active-bgcolor: rgba(55, 65, 81, 1);
/* Light */ --light-color: rgba(55, 65, 81, 1); --light-bgcolor: rgba(243, 244, 246, 1); --light-active-bgcolor: rgba(209, 213, 219, 1);
/* Blue */ --blue-color: rgba(165, 180, 252, 1); --blue-bgcolor: rgba(49, 46, 129, 1); --blue-active-bgcolor: rgba(67, 56, 202, 1);}
#dark:checked ~ .nav { --light: var(--OFF); --dark: var(--ON); --blue: var(--OFF);}
/* 默認爲Light */#light:checked ~ .nav { --light: var(--ON); --dark: var(--OFF); --blue: var(--OFF);}
#blue:checked ~ .nav { --light: var(--OFF); --dark: var(--OFF); --blue: var(--ON);}


效果以下:


Demo: https://codepen.io/airen/full/OJbEzzw

示例中沒有使用任何 JavaScript 代碼。是否是頗有意思。要是感興趣的話,你也能夠嘗試着擼擼這樣的效果。


因爲篇幅緣由,本文只介紹部分屬性,剩餘部分請期待下期的《詳解16個 CSS 新特性(2021最新版-下)》。


✿  拓展閱讀


做者| 大貘
編輯| 橙子君
出品| 阿里巴巴新零售淘系技術


本文分享自微信公衆號 - 淘系技術(AlibabaMTT)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索