CSS 即將支持嵌套,SASS/LESS 等預處理器已無用武之地?

最近,有一則很是振奮人心的消息,CSS 即將原生支持嵌套 -- Agenda+ to publish FPWD of Nesting,表示 CSS 嵌套規範即將進入規範的 FWPD 階段。css

目前對應的規範爲 -- CSS Nesting Modulehtml

隨着 CSS 自定義屬性(CSS Variable)的大規模兼容,到現在 CSS 即將支持嵌套,一些預處理器的核心功能已經被 CSS 原生支持,這是否表示 SASS/LESS 等預處理器已無用武之地?即將被淘汰了?前端

規範的幾個階段

首先簡單介紹一下,一個規範從提出到落地,會經歷的一些階段:git

  1. 編輯草案 Editor's Draft (ED)
  2. 工做草案 Working Draft (WD)
  3. 過渡-最後通告工做草案 Transition – Last Call Working Draft (LCWD)
  4. 候選推薦標準 Candidate Recommendation (CR)
  5. 過渡-建議推薦標準 Transition – Proposed Recommendations (PR)
  6. 推薦標準 Recommendation (REC)

上文說的,即將進入 FPWD,只是處於規範的第 2 個階段 WD 階段,FPWD 表示第一次公開工做草案( First Public Working Draft (FPWD))。FPWD 後面還會有數個工做草案,會處理來自 CSSWG 內部和小組外部更普遍社會的反饋。完善規範的設計。github

也就是說,目前來看,即使後面的流程順利,要等到瀏覽器大範圍實現該規範到能落地的那天還有很是長一段時間。瀏覽器

除此以外,我以爲 SASS\LESS 等預處理器還有一些比較有意思的功能(函數),是即使原生 CSS 支持了自定義屬性和嵌套以後依舊欠缺的,我簡單羅列羅列個人見解。sass

for() 循環函數

目前,原生 CSS 依舊不支持循環函數。markdown

可是其實在預處理器中,循環還算是比較經常使用的一個功能。考慮下面這種佈局:app

ul 下面有多個 li,每一個 li 的高度遞增 20px,一個一個寫固然也能夠,可是有了循環其實能極大減小工做量:less

<ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
</ul>
複製代碼

若是沒有預處理器,咱們的 CSS 多是這樣的:

ul {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
}
li {
    width: 50px;
    background: #000;
}
li:nth-child(1) {
    height: 20px;
}
li:nth-child(2) {
    height: 40px;
}
// ... 3~9
li:nth-child(10) {
    height: 200px;
}
複製代碼

若是利用 SASS 預處理器,能夠簡化成:

ul {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
}
li {
    width: 50px;
    background: #000;
}
@for $i from 1 through 10 {
    li:nth-child(#{$i}) {
        height: calc(#{$i} * 20px);
    }
}
複製代碼

固然,除此以外,在很是多的複雜 CSS 動畫效果中,循環是很是很是經常使用的功能。

譬如一些粒子動畫,咱們一般可能須要去操縱 50100 個粒子,也就是 50100 個 div 的樣式,甚至更多,若是沒有循環,一個一個去寫效率會大打折扣。

利用預處理器循環功能實現的一些效果展現

下面我簡單羅列一些我實現過的,運用到了 CSS 預處理器循環功能的動畫效果。

像上面這個使用純 CSS 實現的火焰效果,其中的火焰的動態燃燒效果。實際上是經過大量的細微粒子的運動,配合濾鏡實現。

其中使用到了 SASS 的循環函數的片斷:

@for $i from 1 to 200 {
    .g-ball:nth-child(#{$i}) {
        $width: #{random(50)}px;
        
        width: $width;
        height: $width;
        left: calc(#{(random(70))}px - 55px);
    }
    
    .g-ball:nth-child(#{$i}) {
        animation: movetop 1s linear -#{random(3000)/1000}s infinite;
    }
}
複製代碼

嗯哼,上面的循環是循環了 200 次之多,若是真要一個一個寫,工做量仍是很是巨大的。上述效果的完整代碼,你能夠戳這裏:

CodePen Demo -- CSS Candles

if() 條件語句

接下來一個就是 if() 條件語句。

其實,CSS 中有一類很是相似條件語句的寫法,也就是媒體查詢 @media 以及 特性檢測 @supports 語句,目前 CSS 中支持的相似條件選擇的一些寫法以下:

@support 條件語句

CSS @supports 經過 CSS 語法來實現特性檢測,並在內部 CSS 區塊中寫入若是特性檢測經過但願實現的 CSS 語句。

div {
    position:fixed;
}
 
@supports (position:sticky) {
    div {
        position:sticky;
    }
}
複製代碼

上述 CSS 語句的意思是若是客戶端支持 position:sticky,則採用 position:sticky,不然,就是 position:fixed

關於 CSS 特性檢測的深刻講解,你能夠看看個人這篇文章:深刻探討 CSS 特性檢測 @supports 與 Modernizr

@media 條件語句

另一種常見的條件語句就是媒體查詢,這個你們仍是比較熟悉的。

若是當前設備知足一種什麼條件,則怎麼樣怎麼樣。

article {
  padding: 4rem;
}
@media screen and (min-width: 900px) {
  article {
    padding: 1rem 3rem;
  }
}
複製代碼

嗯,而且,上述的兩種條件語句能夠互相嵌套使用:

@supports (display: flex) {
  @media screen and (min-width: 900px) {
    article {
      display: flex;
    }
  }
}
複製代碼

不過,上述兩種畢竟不是嚴格意義上的咱們期待的 if() 語句。

好久以前,社區就有聲音(css-values - if() function),提議 CSS 規範中實現 if() 條件語句,相似於這樣:

.foo {
  --calc: calc(10 * (1vw + 1vh) / 2);
  font-size: if(var(--calc) < 12px, 12px, var(--calc));
}
複製代碼

能夠看到這一語句 if(var(--calc) < 12px, 12px, var(--calc)) 相似於一個三元語句,仍是比較好理解的。

然而,上述的條件語句一直沒獲得支持的緣由,在 scss-values - if() function 能夠略窺一二。

緣由是 CSS 一直在儘可能避免在屬性當中產生任意依賴。在 CSS 中,屬性之間自己存在一些隱式依賴,譬如 em 單位長度受到父元素的 font-size 的影響,若是做者可以添加任意依賴關係(經過 if() 條件語句),那麼將會致使一些問題。

原文是:this, unfortunately, means we're adding arbitrary dependencies between properties, something we've avoided doing so far because it's, in general, unresolvable. Custom properties can arbitrarily refer to each other, but they're limited in what they can do, and have a somewhat reasonable "just become invalid" behavior when we notice a cycle. Cycles are more difficult to determine for arbitrary CSS, and can happen much more easily, because there are a number of existing, implicit between-property dependencies. For example, anything that takes a length relies on font-size (due to em), and so you can't have a value in font-size that refers to a property that takes a length (so no adjusting font-size to scale with width!). We add new dependencies of this sort over time (such as adding the lh unit, which induces a dependency on line-height); if authors could add arbitrary dependencies, we'd be unable to add new implicit ones for fear of breaking existing content (by forming cycles that were previous valid and non-cyclic).

因此,CSS 中的直接 if() 語句一直沒有獲得實現。

SASS 等預處理器中的 if() 語句

最後,咱們來看看預處理器中對 if() 的運用,因爲 SASS 等預處理器最終仍是要編譯成 CSS 文件,因此 if() 其實並不太經常使用。由於 SASS 中的 if() 也沒法實現相似上述說的 font-size: if(var(--calc) < 12px, 12px, var(--calc)) 這種功能。

在 SASS 中,我認爲最經常使用的 if() 可能也就是這種場景:

@mixin triangle($size, $color, $direction) {
  height: 0;
  width: 0;

  border-color: transparent;
  border-style: solid;
  border-width: $size;

  @if $direction == up {
    border-bottom-color: $color;
  } @else if $direction == right {
    border-left-color: $color;
  } @else if $direction == down {
    border-top-color: $color;
  } @else if $direction == left {
    border-right-color: $color;
  } @else {
    @error "Unknown direction #{$direction}.";
  }
}

.next {
  @include triangle(5px, black, right);
}
複製代碼

上述代碼是對 CSS 實現三角形的一個封裝,經過傳入的參數,實現不一樣方向、顏色、大小的三角形。也就是預處理器中 if() ,更多的完成一些函數功能的封裝,方便複用。

實際上述的代碼會被編譯成:

.next {
  height: 0;
  width: 0;
  border-color: transparent;
  border-style: solid;
  border-width: 5px;
  border-left-color: black;
}
複製代碼

Random() 隨機函數

OK,接下來這個是隨機函數,是我我的在 SASS 等預處理器中最經常使用的一個函數。目前原生 CSS 不支持任意形式的隨機。

在 CSS 動畫效果中,很是多的因素咱們不但願是一成不變的,咱們但願的是,一些屬性的值的產生由咱們設定一個基礎規則,一個範圍中獲得,這樣每次刷新都能產生不一樣的效果。

最多見的莫過於不一樣的顏色、不一樣的長度、不一樣的數量等等等等。

譬以下面這個使用 CSS 實現的效果:夏日夕陽圖

咱們經過隨機,每次刷新均可以獲得高度/寬度不同,位置不同的 div 塊,利用隨機的特性,繪製一幅幅不同的效果圖:

DEMO -- 夏日夕陽圖

目前原生 CSS 不支持任意形式的隨機。使用預處理器,也只能是在編譯前編寫隨機函數,SASS 中比較經常使用的隨機函數的一些寫法:

$r: random(100);
複製代碼

random() 是 SASS 支持的一種函數,上述 $r 就能獲得一個 0 ~ 100 的隨機整數。

利用 random(),就能封裝出各類隨機函數,譬如隨機顏色:

@function randomNum($max, $min: 0, $u: 1) {
    @return ($min + random($max)) * $u;
}

@function randomColor() {
    @return rgb(randomNum(255), randomNum(255), randomNum(255));
}

div {
    background: randomColor();
}
複製代碼

關於原生 CSS 實現 random() 的一些思考

下面這個是社區對原生 CSS 實現 random() 函數的一些思考,感興趣的能夠猛擊:

[css-values] random() function

簡單搬運其中一些比較有意思的觀點。

假設 CSS 原生實現了 random() 函數,譬以下述這個寫法:

<p class="foo">123</p>
<p class="foo">456</p>
<p class="foo">789</p>
複製代碼
.foo:hover { 
    color: rgb(random(0, 255), 0, 0); 
}
複製代碼

假設其中 ramdom() 是原生 CSS 實現的隨機函數,有一些事情是須要被解決或者獲得你們的承認的:

  1. random(0, 255) 的值在何時被肯定,是在每一次 CSS 解析時,仍是每一次被應用觸發時?
  2. 對於上述 DEMO,3 個 .foocolor 值是否同樣?
  3. 對於反覆的 hover,取消 hover 狀態,random(0, 255) 的值是否會發生變化?

上述的問題能夠歸結於若是 CSS 原生支持隨機,隨機值的持久化和更新是必需要解決的問題。總之,目前看來,將來 CSS 原生支持隨機的可能性仍是很大的。

工具函數:顏色函數、數學函數

最後,咱們再來看看一些有意思的工具函數。目前原生 CSS 暫時不支持一些比較複雜的顏色函數和數學函數。可是預處理器都帶有這些函數。

在我以前的一篇關於陰影的文章中 -- 你所不知道的 CSS 陰影技巧與細節,介紹過一種利用多重陰影實現立體陰影的效果,譬如咱們要實現下面這個效果:

其中的陰影的顏色變化就藉助了 SASS 的顏色函數

  • fade-out 改變顏色的透明度,讓顏色更加透明
  • desaturate 改變顏色的飽和度值,讓顏色更少的飽和
@function makelongrightshadow($color) {
    $val: 0px 0px $color;

    @for $i from 1 through 50 {
        $color: fade-out(desaturate($color, 1%), .02);
        $val: #{$val}, #{$i}px #{$i}px #{$color};
    }

    @return $val;
}

p{
   text-shadow: makelongrightshadow(hsla(14, 100%, 30%, 1));
}
複製代碼

固然,除了上述的兩個顏色函數,SASS 還提供了很是多相似的顏色相關的函數,能夠看看這裏:Sass基礎—顏色函數

除了顏色,數學函數也是常常在 CSS 效果中會須要用到的。

我在這篇文章中 -- 在 CSS 中使用三角函數繪製曲線圖形及展現動畫,專門講了如何利用 SASS 等預處理器實現三角函數,以實現曲線線條,實現一些有意思的效果,像是這樣:

固然,目前 SASS 也不支持三角函數,可是咱們能夠利用 SASS function,實現一套三角函數代碼:

@function fact($number) {
    $value: 1;
    @if $number>0 {
        @for $i from 1 through $number {
            $value: $value * $i;
        }
    }
    @return $value;
}

@function pow($number, $exp) {
    $value: 1;
    @if $exp>0 {
        @for $i from 1 through $exp {
            $value: $value * $number;
        }
    }
    @else if $exp < 0 {
        @for $i from 1 through -$exp {
            $value: $value / $number;
        }
    }
    @return $value;
}

@function rad($angle) {
    $unit: unit($angle);
    $unitless: $angle / ($angle * 0 + 1);
    @if $unit==deg {
        $unitless: $unitless / 180 * pi();
    }
    @return $unitless;
}

@function pi() {
    @return 3.14159265359;
}

@function sin($angle) {
    $sin: 0;
    $angle: rad($angle);
    // Iterate a bunch of times.
    @for $i from 0 through 20 {
        $sin: $sin + pow(-1, $i) * pow($angle, (2 * $i + 1)) / fact(2 * $i + 1);
    }
    @return $sin;
}

@function cos($angle) {
    $cos: 0;
    $angle: rad($angle);
    // Iterate a bunch of times.
    @for $i from 0 through 20 {
        $cos: $cos + pow(-1, $i) * pow($angle, 2 * $i) / fact(2 * $i);
    }
    @return $cos;
}

@function tan($angle) {
    @return sin($angle) / cos($angle);
}
複製代碼

就目前原生 CSS 而言,在數學函數等方面其實已經作出了很是多的努力,譬如:

  • 基礎運算函數 calc()
  • 比較函數 max()min()clamp()

等兼容性已經逐漸鋪開,能夠開始大規模使用,而相似於

  • 指數函數 pow()sqrt()hypot()log()exp()
  • 三角函數 sin()con()tan()
  • 階梯函數 round()mod()rem()

也在規範 CSS Values and Units Module Level 4 中被說起定義,相信不久的未來也會逐漸落地。

關於社區對數學函數的一些討論,感興趣的也能夠看看這裏:Mathematical Expressions

總結一下

好了,綜上總結一下,就目前而言,我以爲 SASS/LESS 等預處理器在不少方面仍是有有用武之地的,在上述的一些功能原生 CSS 沒有徹底落地以前,預處理器能必定程度上彌補 CSS 的不足。

而且,除去上述說的一些我我的認爲比較重要有意思的功能、函數以外,預處理器其它一些核心功能,譬如 extend、mixins 等也能有效的提高開發時的效率。

因此,在將來的一段時間內,我認爲預處理器仍是能和 CSS 友好共存~

最後

好了,本文到此結束,但願對你有幫助 :)

想 Get 到最有意思的 CSS 資訊,千萬不要錯過個人公衆號 -- iCSS前端趣聞 😄

更多精彩 CSS 效果能夠關注個人 CSS 靈感

更多精彩 CSS 技術文章彙總在個人 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

若是還有什麼疑問或者建議,能夠多多交流,原創文章,文筆有限,才疏學淺,文中如有不正之處,萬望告知。

相關文章
相關標籤/搜索