---
title: '2020/02/22 Sass/Scss 教學筆記: 有效率的切版(下) for迴圈、each、if...else 與 function'
disqus: hackmd
---
2020/02/22 Sass/Scss 教學筆記: 有效率的切版(下) for迴圈、each、if...else 與 function
===
綱要
[TOC]
本週開始我們正式進入了用程式邏輯來撰寫樣式的篇章,要做到高效率切版就必須建立起複用性高、且容易擴充的共用組件,而在這之後的課程中也會陸續帶領同學們建立自己的 Sass Library。
這周我們就先來看看如何利用程式邏輯幫助建構樣式吧!
@for迴圈的應用
---
語法: **`@for $var from Num1 through/to Num2`**
`$var`可以自定義變數,每次回傳數字類型的結果。
`through` 包含`Num2`在內的範圍
`to` 不包含`Num2`在內,例如`1 to 24`只會回傳到23為止
**flex示例**
```sass=
@for $i from 0 through 24 {
.flex_#{$i} {
flex: $i;
}
}
```
```css=
/* 編譯後的結果 */
.flex_1 {
flex: 1;
}
.flex_2 {
flex: 2;
}
.flex_3 {
flex: 3;
}
...
...
```
**padding 內留白的示例**
```sass=
@for $i from 0 through 4 {
/* 複習! #{} 的字符代表連接變數使用,{}中可以有運算式 */
[data-inset="#{$i * 0.5 + 'rem'}"] {
padding: #{$i * 0.5 + 'rem'};
box-sizing: border-box;
}
}
```
```css=
/* 編譯後的結果 */
[data-inset="0rem"] {
padding: 0rem;
}
[data-inset="0.5rem"] {
padding: 0.5rem;
}
...
...
[data-inset="2rem"] {
padding: 2rem;
}
```
---
@each的應用
---
語法: **`@each $var in Array`**
`$var`可以自定義變數,每次回傳`Array`對應的值。
`Array` 陣列,可以直接在`in`的後面接`'icon_1', 'icon_2'...`
或者直接另外定義變數存放:
```sass=
$iconSet: ['icon_1', 'icon_2', 'icon_3'];
```
`@each`會自動以Array中有幾個值,每次由左至右回傳對應位置的值
**圖片路徑示例: 不同圖檔名稱的分配**
```sass=
$iconSet: ['icon_1', 'icon_2', 'icon_3'];
@each $currentIcon in $iconSet {
.icon_#{$currentIcon} {
background: url('../img/#{$currentIcon}.svg') no-repeat center;
}
}
```
```css=
/* 編譯後的結果 */
.icon_icon_1 {
background: url("../img/icon_1.svg") no-repeat center;
}
.icon_icon_2 {
background: url("../img/icon_2.svg") no-repeat center;
}
.icon_icon_3 {
background: url("../img/icon_3.svg") no-repeat center;
}
```
**圖片路徑示例: 加上不同裝置版本路徑的分類**
如果你的圖檔包需要拆分不同裝置使用的路徑,`@each`裡面還可以再有一層`@each`做巢狀結構
```sass=
$iconPath: ['pc', 'mobile', 'app'];
$iconSet: ['icon_1', 'icon_2', 'icon_3'];
@each $currentPath in $iconPath {
@each $currentIcon in $iconSet {
.icon_#{$currentPath}_#{$currentIcon} {
background: url('../img/#{$currentPath}/#{$currentIcon}.svg') no-repeat center;
}
}
}
```
```css=
/* 編譯後的結果 */
.icon_pc_icon_1 {
background: url("../img/pc/icon_1.svg") no-repeat center;
}
.icon_pc_icon_2 {
background: url("../img/pc/icon_2.svg") no-repeat center;
}
.icon_pc_icon_3 {
background: url("../img/pc/icon_3.svg") no-repeat center;
}
.icon_mobile_icon_1 {
background: url("../img/mobile/icon_1.svg") no-repeat center;
}
.icon_mobile_icon_2 {
background: url("../img/mobile/icon_2.svg") no-repeat center;
}
.icon_mobile_icon_3 {
background: url("../img/mobile/icon_3.svg") no-repeat center;
}
.icon_app_icon_1 {
background: url("../img/app/icon_1.svg") no-repeat center;
}
.icon_app_icon_2 {
background: url("../img/app/icon_2.svg") no-repeat center;
}
.icon_app_icon_3 {
background: url("../img/app/icon_3.svg") no-repeat center;
}
```
:::info
以下是@each高級用法,我們可以在這定義類似JSON MAP的組件來模組化分配的內容。
結構會稍微有點複雜,但各位同學可以斟酌參考這個例子。
:::
**@each map-get()做按鈕樣式的例子**
Sass自從更新了3.3ver後多了組件定義取值的方法,我們先建立一個樣式組件:
```sass=
$btnComponent:(
submit:(
class: 'submit',
color: #fff,
bgColor: #111199,
borderColor: #ccc
),
cancel:(
class: 'cancel',
color: #333,
bgColor: #fefefe,
borderColor: #dedede
),
danger:(
class: 'danger',
color: #fff,
bgColor: #ff2200,
borderColor: #ff0000
)
);
```
調用時的語法稍有改變:`@each $name, $value in $config`
```sass=
/* $name 指的就是 $btnComponent 中的submit、cancel、以及danger*/
@each $name, $value in $btnComponent {
// 使用 map-get() 將組件內的樣式取出
$class: map-get($value, class);
$color: map-get($value, color);
$bgColor: map-get($value, bgColor);
$borderColor: map-get($value, borderColor);
// 分配到指定的class上
.btn-#{$class}{
color: $color;
background-color: $bgColor;
border-color: $borderColor;
&:hover{
/* 加亮函數lighten */
background-color: lighten($bgColor, 5%);
/* 加深函數darken */
border-color: darken($borderColor, 5%);
}
}
}
```
```css=
/* 編譯後的結果 */
.btn-submit {
color: #fff;
background-color: #111199;
border-color: #ccc;
}
.btn-submit:hover {
background-color: #1414b0;
border-color: #bfbfbf;
}
.btn-cancel {
color: #333;
background-color: #fefefe;
border-color: #dedede;
}
.btn-cancel:hover {
background-color: white;
border-color: #d1d1d1;
}
.btn-danger {
color: #fff;
background-color: #ff2200;
border-color: #ff0000;
}
.btn-danger:hover {
background-color: #ff381a;
border-color: #e60000;
}
```
進階的用法看起來稍微有點複雜,但說穿了只是把對應的名稱和值配出去給目標class而已。
---
@if的應用
---
語法: **`@if $var == (>, <, >=, <=, !=) value (and, or)(value2)`**
`==` 等於
`>` 大於
`>=` 大於或等於
`<` 小於
`<=` 小於或等於
`!=` 不等於
`and` 以及
`or` 或者
`$var`為自定義變數,判別是否符合`Value`
當有其他條件時,也可使用`@else`或`@else if`分別表示 2 個以上條件。
但需要注意`@else if`會編譯出第一個符合`value`的結果,不會把所有符合條件的樣式做編譯。
而`@if`後頭又接了`@if`則會把符合的值一併編譯,造成同樣的屬性會出現兩個以上不同的值。
舉個例子:
```sass=
@for $i from 0 through 3 {
.flex_#{$i} {
flex: $i;
@if $i == 0 {
height: 100%;
} @else if $i >= 2 {
color: #fff;
} @else if $i != 3 {
color: #333;
}
}
}
```
```css=
/* 編譯後的結果 */
.flex_0 {
flex: 0;
height: 100%; /* $i = 0 時加入的條件 */
}
.flex_1 {
flex: 1;
color: #333; /* $i 不等於 3 時加入的條件 */
}
.flex_2 {
flex: 2;
color: #fff; /* $i 大於或等於 2 時加入的條件 */
}
.flex_3 {
flex: 3;
color: #fff;
}
```
`@else if`看起來相當的正常,但如果後面接的是`@if`呢?
```sass=
@for $i from 0 through 3 {
.flex_#{$i} {
flex: $i;
@if $i == 0 {
height: 100%;
}
@if $i >= 2 {
color: #fff;
}
@if $i != 3 {
color: #333;
}
}
}
```
```css=
/* 編譯後的結果 */
.flex_0 {
flex: 0;
height: 100%; /* $i = 0 時加入的條件 */
color: #333; /* $i 不等於 3 時加入的條件 */
}
.flex_1 {
flex: 1;
color: #333; /* $i 不等於 3 時加入的條件 */
}
.flex_2 {
flex: 2;
color: #fff; /* $i 符合大於或等於 2 的條件 */
color: #333; /* $i 符合不等於 3 的條件 */
}
.flex_3 {
flex: 3;
color: #fff;
}
```
看得出來`$i = 2`時`color`被編譯了兩次結果對吧?
而瀏覽器會以最後一個結果為主,這就可能造成不是我們要的樣式覆蓋了正確的樣式,也會讓編譯資源佔據不少垃圾影響效能。
**根據不同版本提供不同字型的例子:**
假設現在我們有一包字體對照檔
```sass=
/* font.scss */
@charset "UTF-8";
@font-face {
font-family: "pc_iconfonts";
src:url("fonts/pc_iconfonts.eot");
src:url("fonts/pc_iconfonts.eot?#iefix") format("embedded-opentype"),
url("fonts/pc_iconfonts.woff") format("woff"),
url("fonts/pc_iconfonts.ttf") format("truetype"),
url("fonts/pc_iconfonts.svg#iconfonts") format("svg");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "mobile_iconfonts";
src:url("fonts/mobile_iconfonts.eot");
src:url("fonts/mobile_iconfonts.eot?#iefix") format("embedded-opentype"),
url("fonts/mobile_iconfonts.woff") format("woff"),
url("fonts/mobile_iconfonts.ttf") format("truetype"),
url("fonts/mobile_iconfonts.svg#iconfonts") format("svg");
font-weight: normal;
font-style: normal;
}
...
...
```
```sass=
/* page.scss */
@import 'font'
$fontName: 'iconFonts';
$path: ['pc', 'mobile', 'app'];
@each $currentPath in $path {
.page_#{$currentPath} {
font-family: $currentPath + '_' + $fontName;
/* app 用不到這兩個屬性所以我們設判斷式過濾 */
@if $currentPath != 'app' {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
}
```
```css=
/* 編譯後的結果 */
.page_pc {
font-family: "pc_iconFonts";
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.page_mobile {
font-family: "mobile_iconFonts";
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.page_app {
font-family: "app_iconFonts";
}
```
---
@function的應用
---
語法: **`@function $var(args){ @return }`**
`$var`為自定義變數,後綴加上一個以上的參數`args`,通常作為計算用途。
**計算padding的例子**
```sass=
@function setPadding($topBottom, $leftRight){
/* 直接return也可以,為了讓大家理解可以在裡面定義其他變數 */
$conbine: #{$topBottom * 1rem + ' ' + $leftRight * 1rem};
@return $conbine;
}
.container {
padding: setPadding(2, 1); // 單位:rem
}
```
```css=
/* 編譯後的結果 */
.container {
padding: 2rem 1rem;
}
```
---
本日後記
---
[回家作業: RWD手機網頁排版](https://docs.google.com/document/d/1szOUUgDaRjo0Hjw_EDFlwEv8u0-fNdEFlGrvg7nVyUw/edit?usp=sharing)
作業不要忘了下週 2/29 在課堂上繳交喔!每週都讓自己變強是最有成就感的事情!
從本週開始,大家應該都可以感覺得出來使用程式邏輯管理CSS是什麼樣的感受。
一直以來寫程式最容易上手的原則就是 **Write less, do more.**
等到累積足夠的經驗後能處理更多稍稍複雜的需求,回過神來發現自己其實已經走了很長一段路。
---
###### tags: `Scss` `flexbox` `data-attribute`