實現一個最小的 CSS 響應式佈局系統

陽光裏她在院子中央晾曬着衣裳 / 在四季的風中她散着頭髮安慰着時光css

——趙雷《南方姑娘》前端

響應式佈局系統,在如今流行的 CSS 框架中已經很是常見了。它主要由容器類和約定一行列數的柵格系統組成,組成了一個框架的骨架。bootstrap

在流行的前端框架 Bootstrap 和 Bulma CSS 中,就有體現。像 Bootstrap 的 .container.row.col;還有 Bulma CSS 的 .containercolumnscolumn 都是表示這類佈局系統。雖然名稱不同,但原理都是相同的。前端框架

隨着 Flex 佈局的普及,幾乎現代的柵格系統的實現都選擇使用這一靈活的佈局方式。數據結構

如今就來看一下,怎樣實現一個最小的 CSS 響應式佈局系統吧。框架

首先從容器提及。ide

爲了保證明現代碼的簡潔,本文將使用 SCSS 來寫。若是你對 SCSS 還不熟悉,沒有關係,行文中會對使用到的知識點作介紹。佈局

容器

容器主要用來包裹網頁的主要內容,常見效果就是將內容居中地顯示在屏幕中間。flex

咱們使用 .container 來約定容器。spa

首先,容器是水平居中的,這一塊樣式較爲容易:

.container {
    margin-left: auto;
    margin-right: auto;
}
複製代碼

所謂的響應式容器,就是根據不一樣的斷點(breakpoints),也就是當前的視口寬度,來決定容器使用的 max-width 值。

這裏咱們借鑑了 Bootstrap 中對斷點的定義,根據視口寬度,分爲如下幾類設備:

  1. 超小屏(Extra small screen),日常手機,範圍是 [0, 576px)
  2. 小屏(Small screen),大手機,範圍是 [576px, 768px)
  3. 中屏(Medium screen),平板電腦,範圍是 [768px, 992px)
  4. 大屏(Large screen),桌面電腦,範圍是 [992px, 1200px)
  5. 超大屏(Extra large screen),大桌面電腦,範圍是 [1200px, +∞)

針對斷點定義,聲明一個變量 $breakpoints

$breakpoints: (
    // Extra small screen / phone
    xs: 0,
    // Small screen / phone
    sm: 576px,
    // Medium screen / tablet
    md: 768px,
    // Large screen / desktop
    lg: 992px,
    // Extra large screen / wide desktop
    xl: 1200px
);
複製代碼

$breakpoints 稱爲「列表」,是 SCSS 提供給咱們的數據結構。由一個個 key: value 鍵值對組成。上例中的 key 表示的是設備有效範圍的起始點。

不一樣的設備下,容器有不一樣的 max-width 值。因此,這裏咱們再聲明一個表示容器寬度的變量 $container-max-widths

$container-max-widths: (
    xs: none,
    sm: 540px,
    md: 720px,
    lg: 960px,
    xl: 1140px
);
複製代碼

這裏的 $container-max-widths 也是個列表,這裏的 key 表示某個設備下容器的最大寬度。好比,在超大屏設備下,容器的最大寬度是 1140px,而在日常手機下,不設置容器的最大寬度,爲默認值 none

有了實現的思路,接下來就着手實現。

咱們就能夠藉助媒體查詢指令 @media,依據視口寬度的範圍,給予 .container 不一樣的 max-width 值。

@each $device, $breakpoint in $breakpoints {
    @media only screen and (min-width: $breakpoint) {
        .container {
            max-width: map-get($container-max-widths, $device);
        }
    }
}
複製代碼

7 行代碼搞定!

下面解釋下上面的代碼。

咱們對列表遍歷,使用的是 @each...in 語法,每一次遍歷取出對應的 key、value,獲得當前的 $device$breakpointmap-get 是 SCSS 提供的用來操做列表的方法:根據 key 取出 value。好比,當 $device 值爲 xs 的時候,map-get($container-max-widths, $device) 對應值爲 none;當 $device 值爲 sm 的時候,map-get($container-max-widths, $device) 對應值爲 540px,以此類推。

@media only screen and (min-width: $breakpoint) { ... } 中包含的代碼,表示從當前設備斷點開始處,應用的 CSS 樣式。當咱們同時按照從小到大的順序設置兩個斷點的媒體查詢時,後者會覆蓋前者的樣式,這是實現不一樣視口下,具備不一樣寬度容器的核心原理。

接下來,將獲得的寬度值賦給容器的 max-width 屬性就能夠了。

到如今爲止,咱們就寫出了一個響應式容器了,咱們總攬下代碼:

$breakpoints: (
    // Extra small screen / phone
    xs: 0,
    // Small screen / phone
    sm: 576px,
    // Medium screen / tablet
    md: 768px,
    // Large screen / desktop
    lg: 992px,
    // Extra large screen / wide desktop
    xl: 1200px
);

$container-max-widths: (
    xs: none,
    sm: 540px,
    md: 720px,
    lg: 960px,
    xl: 1140px
);

.container {
    margin-left: auto;
    margin-right: auto;
}

@each $device, $breakpoint in $breakpoints {
    @media only screen and (min-width: $breakpoint) {
        .container {
            max-width: map-get($container-max-widths, $device);
        }
    }
}
複製代碼

點擊這裏,查看效果。

下面再來介紹 12 列柵格佈局。

12 列柵格佈局

先使用 Flex 佈局,寫一個最簡的等寬佈局。

.row {
    display: flex;
    
    .col {
        flex-grow: 1;
        flex-basis: 0;
    }
}
複製代碼

沒錯,這就是使用 Flex 佈局實現一個等寬佈局的全部代碼了。若是不考慮中間的空白行,只須要 7 行代碼。

這裏的原理是,咱們將全部 Flex 項目的 flex-basis 設置爲 0 了,就是說這些 Flex 項目在 grow 或 shrink 以前都沒有寬度,是同樣長的。這樣最終計算出來的主軸空間會平均地分配給了每一個 Flex 項目,這樣它們就等寬了。

到這裏,咱們所寫的這個簡易柵格佈局有兩個侷限:

  1. 不能佈局非等寬項目。
  2. 不支持換行。

換行的話很好弄,爲 Flex 容器加個 flex-wrap: wrap 就能夠了。那怎樣處理「非等寬項目」排列布局呢。

爲了能實現非等寬項目的佈局,咱們的思路是:禁用 Flex 項目的伸縮特性,使用百分比 width 指定寬度

首先,禁用 Flex 項目的伸縮特性,使用到的屬性以下:

flex-shrink: 0;
flex-grow: 0;
flex-basis: 0;
複製代碼

這三個屬性等價的快捷寫法是:

flex: none;
複製代碼

而後就是使用百分比 width 指定寬度了。

咱們實現的是一行最多 12 列的柵格佈局。也就是說把一行劃分紅 12 列,每一列的寬度大約佔總寬度的 8.33%。咱們用 .is-列數 指定一個項目佔據的列數:

  • .is-1:佔據 1 列,也就是 1/12 寬
  • .is-2:佔據 2 列,也就是 1/6 寬
  • .is-3:佔據 3 列,也就是 1/4 寬
  • .is-4:佔據 4 列,也就是 1/3 寬
  • .is-5:佔據 5 列,也就是 5/12 寬
  • .is-6:佔據 6 列,也就是 1/2 寬
  • .is-7:佔據 7 列,也就是 7/12 寬
  • .is-8:佔據 8 列,也就是 2/3 寬
  • .is-9:佔據 9 列,也就是 3/4 寬
  • .is-10:佔據 10 列,也就是 5/6 寬
  • .is-11:佔據 11 列,也就是 11/12 寬
  • .is-12:佔據 12 列,也就是佔滿整個寬度

根據這個規律,咱們能夠很容易地寫出柵格佈局代碼:

$columns: 12;

.row {
    display: flex;
    
    .col {
        flex-grow: 1;
        flex-basis: 0;
        
        @for $i from 1 through 12 {
            &.is-#{$i} {
                flex: none;
                width: percentage($i / 12);
            }
        }
    }
}
複製代碼

這裏咱們使用 @for 指令的 @for $var from <start> through <end> 語法,從 1 遞增到 12,定義了 .is-*這一系列類名,原理就是咱們說過的禁用了 Flex 項目的伸縮特性,指定給它百分比寬度。怎麼樣,很簡單吧。

接下來再加上折行(.row.is-multiline)和 Flex 項目偏移(.is-offset-*)的支持。

咱們總攬下代碼:

$columns: 12;

.row {
    display: flex;
    
    &.is-multiline {
        flex-wrap: wrap;   
    }
    
    .col {
        flex-grow: 1;
        flex-basis: 0;
        
        @for $i from 1 through 12 {
            &.is-#{$i} {
                flex: none;
                width: percentage($i / 12);
            }
            &.is-offset-#{$i} {
                margin-left: percentage($i / 12);
            }
        }
    }
}
複製代碼

.is-multiline 是跟隨 .row 一塊兒使用的,獲得的就是 flex-wrap: wrap 的效果;項目偏移則藉助 margin-left 屬性實現。

到這裏,咱們的 12 列柵格佈局就寫完了 ヾ(◍°∇°◍)ノ゙

完整代碼

咱們把上面兩部分的代碼整合起來,就能獲得一個最小的響應式佈局系統了~ O(∩_∩)O

$breakpoints: (
    // Extra small screen / phone
    xs: 0,
    // Small screen / phone
    sm: 576px,
    // Medium screen / tablet
    md: 768px,
    // Large screen / desktop
    lg: 992px,
    // Extra large screen / wide desktop
    xl: 1200px
);

$container-max-widths: (
    xs: none,
    sm: 540px,
    md: 720px,
    lg: 960px,
    xl: 1140px
);

.container {
    margin-left: auto;
    margin-right: auto;
}

@each $device, $breakpoint in $breakpoints {
    @media only screen and (min-width: $breakpoint) {
        .container {
            max-width: map-get($container-max-widths, $device);
        }
    }
}

$columns: 12;

.row {
    display: flex;
    
    &.is-multiline {
        flex-wrap: wrap;   
    }
    
    .col {
        flex-grow: 1;
        flex-basis: 0;
        
        @for $i from 1 through 12 {
            &.is-#{$i} {
                flex: none;
                width: percentage($i / 12);
            }
            &.is-offset-#{$i} {
                margin-left: percentage($i / 12);
            }
        }
    }
}
複製代碼

能夠在此查看效果

固然,更多其餘豐富的功能任君添加,這裏只是提供了一個最簡單的代碼實現。

參考連接

Grid system, by getbootstrap.com

貢獻指北

感謝你花費寶貴的時間⏲️閱讀這篇文章。

若是你以爲這篇文章讓你的生活美好了一點點,歡迎鼓(diǎn)勵(zàn)😀。若是能在文章下面留下你寶貴的評論或意見是再合適不過的了,由於研究證實,參與討論比單純閱讀更能讓人對知識印象深入😉。

(完)

相關文章
相關標籤/搜索