【學習筆記】CSS 佈局

一些常見基本概念

  • 物理像素(physical pixel):又被稱爲設備像素,它是顯示設備中一個最微小的物理部件,每一個像素可根據操做系統設置本身的顏色和亮度,正是這些設備像素的微小距離欺騙了肉眼看到的圖像效果
  • 設備獨立像素(density-independent pixel):設備獨立像素也稱爲密度無關像素,能夠認爲是計算機座標系統中的一個點,這個點表明一個能夠由程序使用的虛擬像素(如 CSS 像素),而後由相關係統轉換爲物理像素
  • CSS 像素:CSS 像素是一個抽像單位,主要使用在瀏覽器上,用來精確度量 Web 頁面上的內容,通常狀況下 CSS 像素被稱爲與設備無關的像素(device-independent pixel),簡稱 DIPs
  • 屏幕密度(PPI):屏幕密度是指一個設備表面上存在的像素數量,它一般以每英寸有多少像素來計算
  • 設備像素比(device pixel ratio):簡稱爲 dpr,其定義了物理像素和設備獨立像素的對應關係
    • 設備像素比 = 物理像素 / 設備獨立像素
    • 在 JS 中能夠經過 window.devicePixelRatio 獲取到當前設備的 dpr
    • 在 CSS 中可經過 -webkit-device-pixel-ratio、-webkit-min-device-pixel-ratio、-webkit-max-device-pixel-ratio 進行媒體查詢,對不一樣 dpr 的設備作一些樣式適配,或使用 resolution | min-resolution | max-resolution 這些比較新的標準方式

image.png

  • 位圖像素
    • 一個位圖像素是柵格圖像(如 png, jpg, gif 等)最小數據單元,每一個位圖像素都包含着一些自身的顯示信息(如顯示位置、顏色值、透明度等)
    • 理論上一個位圖像素對應於一個物理像素,圖片才能獲得完美清晰的展現,以下圖:對於 dpr=2 的 retina 屏幕而言,一個位圖像素對應於 4 個物理像素,因爲單個位圖像素不能夠再進一步分割,因此只能就近取色,從而致使圖片模糊
    • 對於圖片高清問題,比較好的方案就是兩倍圖片(@2x),如 200×300(CSS pixel) img 標籤,需提供 400×600 的圖片

image.png

  • 縮放比 scale:scale = 1/dpr
  • 視窗 viewport:簡單理解 viewport 是嚴格等於瀏覽器窗口,在桌面瀏覽器中 viewport 就是瀏覽器窗口的寬高,但在移動端設備上有點複雜。閱讀推薦:解讀 viewport—網頁自適應移動 app 神器
  • 媒體查詢 @media
    • 媒體查詢由一個可選的媒體類型和零個或多個使用媒體功能的限制了樣式表範圍的表達式組成,例如寬度、高度和顏 色。媒體查詢添加自 CSS3,容許內容的呈現針對一個特定範圍的輸出設備而進行裁剪,而沒必要改變內容自己,很是適 合web網⻚應對不一樣型號的設備而作出對應的響應適配
    • 媒體查詢包含一個可選的媒體類型和,知足 CSS3 規範的條件下,包含零個或多個表達式,這些表達式描述了媒體特徵,最終會被解析爲 true 或 false。若媒體查詢中指定的媒體類型匹配展現文檔所使用的設備類型且全部的表達式的值都是true,則該媒體查詢的結果爲 true,那麼媒體查詢內的樣式將會生效
    <!-- link 元素中的 CSS 媒體查詢 -->
    <link rel="stylesheet" media="(max-width: 800px)" href="example.css" />
    <!-- 樣式表中的CSS媒體查詢 --> 
    <style> @media (max-width: 767px) { ...css代碼... } @media (min-width: 768px) and (max-width: 991px) { ...css代碼... } </style>
    複製代碼

    閱讀推薦:隨方逐圓 -- 全面理解 CSS 媒體查詢css

一些佈局概念

靜態佈局(static layout)html

  • 傳統 Web 設計,網頁上的全部元素的尺寸一概使用 px 做爲單位,無論瀏覽器尺寸具體多少,網頁佈局始終按照最初代碼的佈局來顯示
  • 設計方法
    • pc 端:居中佈局,全部樣式使用絕對寬度/高度 (px) 設計一個 Layout,在屏幕寬高有調整時使用橫向和豎向的滾動條來查閱被遮掩部分
    • 移動設備:另外創建移動網站,單獨設計一個佈局,使用不一樣的域名如 wap. 或 m. 等
  • 優勢:這種佈局方式對設計師和 CSS 編寫者來講都是最簡單的,亦沒有兼容性問題
  • 缺點:這種佈局方式不能根據用戶的屏幕尺寸作出不一樣的表現,固定像素尺寸的網頁是匹配固定像素尺寸顯示器的最簡單辦法,但這種方法不是一種徹底兼容目前網頁的製做方法,須要一些適應未知設備的方法

流式佈局(Liquid Layout)前端

  • 流式佈局(Liquid)的特色(也叫 "Fluid") 是頁面元素的寬度按照屏幕分辨率進行適配調整,但總體佈局不變,表明做如:柵欄系統(網格系統)
  • 使用 % 百分比定義寬度,高度大都是用 px 來固定。可根據可視區域 (viewport) 和父元素的實時尺寸進行調整,儘量的適應各類分辨率,每每配合 max-width/min-width 等屬性控制尺寸流動範圍以避免過大或者太小影響閱讀(圖片等也是如此,width:100%, max-width 通常設定爲圖片自己的尺寸,防止被拉伸而失真)
  • 佈局特色:屏幕分辨率變化時頁面裏元素大小會變化但佈局不變,這就致使若屏幕太大或過小都會致使元素沒法正常顯示
  • 優勢:該佈局方式在 Web 前端開發的早期歷史上用來應對不一樣尺寸的 PC 屏幕(那時屏幕尺寸差別不會太大),在移動端開發也是經常使用佈局方式
  • 缺點:主要問題是若屏幕尺度跨度太大,那在相對其原始設計而言太小或過大的屏幕上不能正常顯示,由於寬度使用 % 百分比定義,但高度和文字大小等大都是用 px 來固定,因此在大屏幕手機下顯示效果會變成有些頁面元素寬度被拉的很長,但高度、文字大小仍是和原來同樣(即這些東西沒法變得「流式」),顯示很是不協調

自適應佈局(Adaptive Layout)android

  • 特色:建立多個靜態佈局,每一個靜態佈局對應一個屏幕分辨率範圍,使用 @media 媒體查詢來切換多個佈局,改變屏幕分辨率能夠切換不一樣的靜態局部(頁面元素位置發生改變),但在每一個靜態佈局中頁面元素不隨窗口大小的調整發生變化,能夠把自適應佈局看做是靜態佈局的一個系列
  • 屏幕分辨率變化時頁面裏面元素的位置會變化而大小不會變化
  • 設計方法:使用 @media 媒體查詢給不一樣尺寸和介質的設備切換不一樣的樣式,在優秀的響應範圍設計下能夠給適配範圍內的設備最好的體驗

響應式佈局(Responsive Layout)css3

  • 響應式設計的目標是確保一個頁面在全部終端上(各類尺寸的PC、手機、手錶、冰箱的 Web 瀏覽器等)都能顯示出使人滿意的效果,對 CSS 編寫者而言,在實現上不拘泥於具體手法,但一般是糅合了流式佈局 + 彈性佈局,搭配媒體查詢技術使用,分別爲不一樣的屏幕分辨率定義佈局(響應式幾乎已經成爲優秀頁面佈局的標準)
  • 佈局特色:每一個屏幕分辨率下面會有一個佈局樣式,即元素位置和大小都會變
  • 優勢:適應 pc 和移動端,若是足夠耐心,效果完美
  • 缺點:
    • 媒體查詢是有限的,便可枚舉出來的只能適應主流的寬高
    • 要匹配足夠多的屏幕大小,工做量不小,設計也須要多個版本

彈性佈局(rem/em 佈局)git

  • rem 是相對於 HTML 元素的 font-size 大小而言的,而 em 是相對於其父元素的(非 font-size 屬性是相對於自身的 font-size )
  • 使用 em 或 rem 相對 % 百分比更加靈活,同時可支持瀏覽器的字體大小調整和縮放等的正常顯示
  • 佈局特色:包裹文字的各元素的尺寸採用 em/rem 作單位,而頁面的主要劃分區域的尺寸仍使用百分數或 px 作單位(同「流式佈局」或「靜態/固定佈局」),早期瀏覽器不支持整個頁面按比例縮放,僅支持網頁內文字尺寸的放大,這種狀況下使用 em/rem 作單位,可使包裹文字的元素隨着文字的縮放而縮放
  • 瀏覽器的默認字體高度通常爲 16px,即 1em: 16px,可是 1:16 的比例不方便計算,爲了使單位 em/rem 更直觀,CSS 編寫者經常將頁面跟節點字體設爲 62.5%,如選擇用 rem 控制字體時需先設置根節點 html 的字體大小,因瀏覽器默認字體大小16px * 62.5% = 10px,這樣 1rem 即是 10px,方便了計算
  • 用 em/rem 定義尺寸的另個好處是更能適應縮進、以字體單位 padding 或 margin、瀏覽器設置字體尺寸等狀況,由於 em/rem 相對於字體大小會同步改變,如:p{ text-indent: 2em; }
  • 使用 rem 單位的彈性佈局在移動端也很受歡迎,淘寶的 Flexible 讓 rem 佈局得以流行開來,而此 Flexible 實現也有一些不足,此外也涌現出了多種實現 rem 佈局的方案,如直接使用 html{ font-size:625%; } 基準值配合 JS 來設置根元素字體大小或者使用媒體查詢來設置根元素字體大小等

傳統常見佈局方式

文檔流佈局

這是最基本的佈局方式,按照文檔的順序一個一個顯示出來,塊元素獨佔一行,行內元素共享一行github

浮動佈局

浮動方式佈局就是使用 float 屬性,使元素脫離文檔流浮動起來web

定位佈局

經過 position 屬性來進行定位瀏覽器

table 佈局

  • 直接用 table 等標籤佈局,table 佈局自動垂直居中
  • table 的好處:在某些場合使用 table 是徹底合適、恰當和準確的,如用 table 作表格,若沒法判斷是否應該使用 table,請問本身幾個問題,若是答案僅僅是「我猜……也許不是」,那麼就不該該用table
    • 「是否這些行或者列的信息共享某一個屬性?好比每行顯示一個學生的信息,全部學生都有個‘姓名’屬性」
    • 「若是我改變了這些行或者列的順序,是否是依然有意義或者有一樣的效果?」
    • 「若是將行變成列或者將列變成行,是否是依然有意義或者有一樣的效果?」
  • 在實際的項目開發過程當中,不建議用 table 進行佈局,緣由以下:
    • table 比其它 html 標記佔更多的字節(形成下載時間延遲,佔用服務器更多流量資源)
    • table 會阻擋瀏覽器渲染引擎的渲染順序(會延遲頁面的生成速度,讓用戶等待更久的時間),table 必須在頁面徹底加載後才顯示,沒有加載完畢前 table 爲一片空白,即須要頁面完畢才顯示,而 div 是逐行顯示,不須要頁面徹底加載完畢就能夠一邊加載一邊顯示
    • table 裏顯示圖片時須要把單個、有邏輯性的圖片切成多個圖(增長設計的複雜度,增長頁面加載時間,增長 http 會話數)
    • 在某些瀏覽器中,table 裏的文字的拷貝會出現問題(會讓用戶不悅)
    • table 會影響其內部的某些佈局屬性的生效(如元素的 height:100%) (限制頁面設計的自由性)
    • 一旦學了 CSS 的知識,會發現使用 table 作頁面佈局會變得更麻煩(先花時間學一些 CSS 知識,會省去你之後大量的時間)
    • table 代碼會讓閱讀者抓狂(不但沒法利用 CSS,並且會不知所云,尤爲在進行頁面改版或內容抽取時)
    • table一旦設計完成就變成死的,很難經過CSS讓它展示新的面貌(CSS ZEN GARDEN:www.csszengarden.com/)

div + css 的佈局較 table 佈局有什麼優勢?
改版時更方便,只要改css文件
頁面加載速度更快、結構化清晰、頁面顯示簡潔,表現與結構相分離
易於優化(seo)搜索引擎更友好sass

flex 佈局

flex 是什麼

2009 年對前端來講是不平凡的一年,HTML5 定稿、ES5.1 發佈、flex 應運而生。flex 是 flexible box 的縮寫,意爲"彈性佈局",天生響應式,生而爲佈局,使用及其簡單,能夠簡便、完整、響應式地實現各類頁面佈局
根據規範中的描述可知道,flexbox 模塊提供了一個有效的佈局方式,即便不知道視窗大小或未知元素狀況之下均可以智能的、靈活的調整和分配元素和空間二者之間的關係。簡單的理解就是能夠自動調整,計算元素在容器空間中的大小

  • flex 是一種新型的佈局方式,使用該佈局方式能夠實現幾乎全部想要的效果。可是要注意其瀏覽器的兼容性,flex 只支持 ie 10+,全部仍是要根據項目狀況使用,具體可查閱:Can I use 查閱兼容性狀況

image.png

FFC (flex formatting context)

  • flexbox 佈局新定義了格式化上下文,相似 BFC(block formatting context),除了佈局和一些細節不一樣之外的一切規則都和 BFC 是相同的

    注意 : 這裏所指的 flexbox 是指設置了 display: flex; 或 display: inline-flex; 的盒子,不是指單單設置了 display: flex; 的盒子

  • 例如,設置了 display: flex; 或 display: inline-flex 的元素,和 BFC 同樣,不會被浮動的元素遮蓋,不會垂直外邊距坍塌等等
  • 對於設置了 display: inline-flex 的盒子來講,能夠類比 display: inline-box; 理解,它不會獨佔一行,可是能夠設置寬和高
  • 與 BFC 的細微區別
    • flexbox 不支持 ::first-line 和 ::first-letter 這兩種僞元素
    • vertical-align 對 flexbox 中的子元素是沒有效果的
    • float 和 clear 屬性對 flexbox 中的子元素是沒有效果的,也不會使子元素脫離文檔流(可是對 flexbox 是有效果的!)
    • 多欄佈局(column-*) 在 flexbox 中也是失效的,即不能使用多欄佈局在flexbox 排列其下的子元素(魚和熊掌不可得兼嘛)
    • flexbox 下的子元素不會繼承父級容器的寬

對於 flex 盒模型的設計指望

  • 在任何流動的方向上(包括上下左右)都能進行良好的佈局
  • 能夠以逆序或以任意順序排列布局
  • 能夠線性的沿着主軸一字排開或沿着側軸換行排列
  • 能夠彈性的在任意的容器中伸縮大小
  • 可使子元素們在容器主軸方向上或在容器側軸方向上進行對齊
  • 能夠動態的沿着主軸方向伸縮子級的尺寸,與此同時保證父級側軸方向上的尺寸

基本概念

  • 在 flex 容器中默認存在兩條軸,水平主軸(main axis) 和垂直的交叉側軸(cross axis),這是默認的設置,固然可經過修改使垂直方向變爲主軸,水平方向變爲交叉軸

  • 容器中的每一個單元塊被稱爲 flex item,每一個項目佔據的主軸空間爲 (main size), 佔據的交叉軸的空間爲 (cross size)

    注意不能先入爲主認爲寬度就是 main size,高度就是 cross size,這個還要取決於主軸方向,若垂直方向是主軸,那項目的高度就是 main size

  • writing-mode 屬性可改變文檔流方向,此時主軸是垂直方向,但實際開發不多遇到這樣場景,所以初學時直接使用水平方向和垂直方向理解不會有任何問題,反而易於理解

image.png

使用 flex 佈局

  • 實現 flex 佈局需先指定一個容器,任何一個容器只要被指定爲 flex 佈局,該容器內部元素就可以使用 flex 進行佈局

    • 給 div 這類塊狀元素元素設置 display: flex 或給 span 這類內聯元素設置 display: inline-flex,Flex佈局即建立!其中,直接設置 display: flex 或 display: inline-flex 的元素稱爲 flex 容器,裏面的子元素稱爲 flex 子項
    • 注意:當時設置 flex 佈局後子元素的 float、clear、vertical-align 的屬性將會失效
  • flex 佈局相關屬性分爲兩撥:一撥做用在 flex 容器上(此處以「父容器」代替),還有一撥做用在 flex 子項(此處以「子容器」代替)上,具體參見下表,不管做用在父容器上,仍是做用在子容器,都是控制子容器的呈現,只是前者控制的是總體,後者控制的是個體

    做用在父容器 做用子容器
    flex-direction order
    flex-wrap flex-grow
    flex-flow flex-shrink
    justify-content flex-basis
    align-items flex
    align-content align-self
  • 做用在父容器上的屬性
    flex-direction

    • 決定主軸的方向(主軸默認是水平方向,從左至右),用來控制子項總體佈局方向,從左往右仍是從右往左,從上往下仍是從下往上。和 CSS 的 direction 屬性相比就是多了個 flex
    // row:默認值,顯示爲行,方向爲當前文檔水平流方向(主軸),默認狀況下是從左往右。若是當前水平文檔流方向是 rtl(如設置 direction: rtl),則從右往左
    // row-reverse:顯示爲行,但方向和 row 屬性值是反的
    // column:顯示爲列,垂直方向,起點在上沿
    // column-reverse:顯示爲列,垂直方向,但方向和 column 屬性值是反的
    flex-direction: row | row-reverse | column | column-reverse;
    複製代碼

    image.png

    flex-wrap

    • 決定子容器是否換行顯示,默認狀況下項目都排在主軸線上,使用 flex-wrap 可實現項目的換行
    // nowrap:默認值,表示單行顯示不換行,即當主軸尺寸固定、空間不足時,項目尺寸會隨之調整而並不會擠到下一行
    // wrap:子容器主軸總尺寸超出父容器時換行,第一行在上方
    // wrap-reverse:寬度不足換行顯示,可是從下往上開始,即本來換行在下面的子項跑到上面
    flex-wrap: nowrap | wrap | wrap-reverse;
    複製代碼

    image.png

    flex-flow

    • 是 flex-direction 和 flex-wrap 的縮寫形式,表示 flex 佈局的 flow 流動特性,默認值爲 row nowrap
    flex-flow: <flex-direction> || <flex-wrap>;
    複製代碼

    justify-content

    • 決定了水平方向子容器的對齊和分佈方式,CSS text-align 有個屬性值爲 justify,可實現兩端對齊
    • justify-content 能夠當作是 text-align 的遠房親戚,不過前者控制 flex 元素的水平對齊外加分佈,後者控制內聯元素的水平對齊
    // flex-start:默認值,邏輯 CSS 屬性值,與文檔流方向相關,默認表現爲左對齊
    // flex-end:邏輯CSS屬性值,與文檔流方向相關,默認表現爲右對齊
    // center:表現爲居中對齊
    // space-between:表現爲兩端對齊,between 是中間的意思,意思是多餘的空白間距只在元素中間區域分配
    // space-around:around 是環繞的意思,意思是每一個子容器兩側都環繞互不干擾的等寬的空白間距,最終視覺上邊緣兩側的空白只有中間空白寬度一半
    // space-evenly:evenly 是勻稱、平等的意思,即視覺上每一個子容器兩側空白間距徹底相等
    
    justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly;
    複製代碼

    image.png

    align-items

    • align-items 中的 items 指的就是子容器,所以 align-items 指的就是 子容器相對於父容器在垂直方向上的對齊方式
    // stretch:默認值,子容器拉伸,即若子容器未設置高度或設爲 auto,將佔滿整個父容器的高度,若子容器設置了高度,則按照設置的高度值渲染,而非拉伸
    // flex-start:邏輯 CSS 屬性值,與文檔流方向相關,默認表現爲容器頂部對齊
    // flex-end:邏輯 CSS 屬性值,與文檔流方向相關,默認表現爲容器底部對齊
    // center:表現爲垂直居中對齊
    // baseline:表現爲全部 =子容器都相對於父容器的基線(字母 x 的下邊緣)對齊
    align-items: stretch | flex-start | flex-end | center | baseline;
    複製代碼

    image.png

    align-content

    • align-content 可當作和 justify-content 是類似且對立的屬性,justify-content 指明水平方向子容器的對齊和分佈方式,而 align-content 則是指明垂直方向每一行子容器的對齊和分佈方式,若全部子容器只有一行,則 align-content 屬性是沒有任何效果的
    • 當 flex-wrap 設置爲 nowrap 時,容器僅存在一根軸線,由於項目不會換行則不會產生多條軸線,當 flex-wrap 設置爲 wrap 時,容器可能會出現多條軸線,這時就須要去設置多條軸線之間的對齊方式了
    // stretch:默認值,每行子容器都等比例拉伸,如若共兩行子容器,則每一行拉伸高度是 50%
    // flex-start:邏輯CSS屬性值,與文檔流方向相關,默認表現爲頂部堆砌
    // flex-end:邏輯CSS屬性值,與文檔流方向相關,默認表現爲底部堆放
    // center:表現爲總體垂直居中對齊
    // space-between:表現爲上下兩行兩端對齊。剩下每一行元素等分剩餘空間
    // space-around:每一行元素上下都享有獨立不重疊的空白空間
    // space-evenly:每一行元素都徹底上下等分
    align-content: stretch | flex-start | flex-end | center | space-between | space-around | space-evenly;
    複製代碼

    image.png

  • 做用在子容器上的屬性
    order

    • 定義子容器在父容器中的排列順序,數值越小,排列越靠前,默認值爲 0
    order: <integer>; /* 整數值,默認值是 0 */
    複製代碼

    image.png

    flex-grow

    • grow 是擴展的意思,擴展的是子容器所佔據的寬度,定義項目的放大比例,擴展所侵=佔的空間就是除去元素外的剩餘空白間隙
    • flex-grow 不支持負值,默認值是 0,表示不佔用剩餘的空白間隙擴展本身的寬度,若 flex-grow 大於0,則父容器剩餘空間的分配就會發生,具體規則以下
      1. 全部剩餘空間總量是 1
      2. 若只有一個子容器設置了 flex-grow 屬性值
        1)若 flex-grow 值小於1,則擴展空間就是總剩餘空間和這個比例的計算值
        2)若 flex-grow 值大於1,則獨享全部剩餘空間
      3. 如有多個子容器設置了 flex-grow 屬性值
        1)若 flex-grow 值總和小於1,則每一個子容器擴展空間就是總剩餘空間和當前元素設置的 flex-grow 比例的計算值
        2)若 flex-grow 值總和大於1,則全部剩餘空間被利用,分配比例就是 flex-grow 屬性值的比例,如全部的子容器都設置 flex-grow: 1,則表示剩餘空白間隙你們等分;若設置的 flex-grow 比例是 1:2:1,則中間的子容器佔據一半的空白間隙,剩下的先後兩個元素等分
      4. 若當全部項目以 flex-basis 的值排列完後發現空間不夠且 flex-wrap:nowrap 時,此時 flex-grow 則不起做用,就須要接下來的 flex-shrink 屬性
    flex-grow: <number>; /* 數值,能夠是小數,默認值是 0 */
    複製代碼

    image.png

    flex-shrink

    • 要處理當父容器空間不足時,單個元素的收縮比例
    • flex-shrink 不支持負值,默認值是 1,即默認全部子容器都會收縮,若設置爲 0,則表示不收縮,保持原始的 fit-content 寬度
    • flex-shrink 的內核跟 flex-grow 神似,flex-grow 是空間足夠時如何利用空間,flex-shrink 則是空間不足時候如何收縮騰出空間
    • 此屬性要生效,父容器的 flex-wrap 屬性要設置爲 nowrap
    • 若只有一個子容器設置了 flex-shrink
      • flex-shrink 值小於 1 則收縮的尺寸不徹底,會有一部份內容溢出父容器
      • flex-shrink 值大於等於1,則收縮徹底,正好填滿父容器
    • 若多個子容器設置了 flex-shrink
      • flex-shrink 值的總和小於 1 則收縮的尺寸不徹底,每一個元素收縮尺寸佔「徹底收縮的尺寸」的比例就是設置的 flex-shrink 的值
      • flex-shrink 值的總和大於 1 則收縮徹底,每一個元素收縮尺寸的比例和 flex-shrink 值的比例同樣
      flex-shrink: <number>; /* 數值,默認值是 1 */
      複製代碼
      image.png

    flex-basis

    • 定義了在分配多餘空間以前,項目佔據的主軸空間,瀏覽器根據這個屬性,計算主軸是否有多餘空間
    • 它能夠設爲跟 width 或 height 屬性同樣的值(如 350px),則項目將佔據固定空間
    • 默認值:auto,項目原本的大小, item 的寬高取決於 width、height 值,沒有設置 width、height 就按內容寬度來
    • 當主軸爲水平方向且設置了 flex-basis 時,項目的 width 設置值會失效,flex 顧名思義就是彈性的意思,所以實際上不建議對 flex 子項使用 width 屬性,由於不夠彈性
    • 當剩餘空間不足時,子容器的實際寬度一般不是設置的 flex-basis,由於 flex 佈局剩餘空間不足的時候默認會收縮
    • 當 flex-basis 值爲 0 % 時,是把該項目視爲零尺寸,即便聲明該尺寸爲 140px,也並無什麼用
    • 當 flex-basis 值爲 auto 時,則根據尺寸的設定值(如爲 100px),則這 100px 不會歸入剩餘空間
    • flex-basis 需跟 flex-grow 和 flex-shrink 配合使用才能發揮效果
    flex-basis: <length> | auto; /* default auto */
    複製代碼

    flex

    • flex 屬性是 flex-grow、flex-shrink、flex-basis的縮寫形式
    • 其中第 2 和第 3 個參數( flex-shrink 和 flex-basis )是可選的,默認值爲0 1 auto
    • flex 默認值等同於 flex: 0 1 auto;
    • flex: none 等同於 flex: 0 0 auto;
    • flex: auto 等同於 flex: 1 1 auto;
    flex: none | auto | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
    
    // 當 flex 取值爲一個非負數字,則該數字爲 flex-grow 值,flex-shrink 取 1,flex-basis 取 0%
    // 以下同等
    .item {flex: 1;}
    .item {
         flex-grow: 1;
         flex-shrink: 1;
         flex-basis: 0%;
    }
    
    // 當 flex 取值爲 0 時,對應的三個值分別爲 0 1 0%
    item {flex: 0;}
    .item {
         flex-grow: 0;
         flex-shrink: 1;
         flex-basis: 0%;
    }
    
    // 當 flex 取值爲一個長度或百分比,則視爲 flex-basis 值,flex-grow 取 1,flex-shrink 取 1
    // 有以下等同狀況(注意 0% 是一個百分比而不是一個非負數字)
    .item-1 {flex: 0%;}
    .item-1 {
         flex-grow: 1;
         flex-shrink: 1;
         flex-basis: 0%;
    }
    
    .item-2 {flex: 24px;}
    .item-2 {
         flex-grow: 1;
         flex-shrink: 1;
         flex-basis: 24px;
    }
    
    // 當 flex 取值爲兩個非負數字,則分別視爲 flex-grow 和 flex-shrink 的值,flex-basis 取 0%,以下是等同的:
    .item {flex: 2 3;}
    .item {
         flex-grow: 2;
         flex-shrink: 3;
         flex-basis: 0%;
    }
    
    // 當 flex 取值爲一個非負數字和一個長度或百分比,則分別視爲 flex-grow 和 flex-basis 的值,flex-shrink 取 1,以下是等同的:
    .item {flex: 11 32px;}
    .item {
         flex-grow: 11;
         flex-shrink: 1;
         flex-basis: 32px;
    }
    複製代碼

    align-self

    • align-self: align-self 指控制單獨某個 flex 子項的垂直對齊方式,父容器上的 align-items 屬性,表示子容器們,是全體;這裏是 self,單獨一個個體
    • 惟一區別就是 align-self 多了個auto(默認值),表示繼承自 flex 容器的 align-items 屬性值,其餘屬性含義如出一轍
  • 補充說明父容器的 flex-wrap 與子容器的 flex-shrink、flex-grow 之間的關係

    • 當 flex-wrap 爲 wrap | wrap-reverse 且子容器寬度和不及父容器寬度時,flex-grow 會起做用,子容器會根據 flex-grow 設定的值放大(爲 0 的項不放大)
    • 當 flex-wrap 爲 wrap | wrap-reverse,且子容器寬度和超過父容器寬度時,首先必定會換行,換行後每一行的右端均可能會有剩餘空間(最後一行包含的子項可能比前幾行少則剩餘空間可能會更大),這時 flex-grow 會起做用,若當前行全部子容器的 flex-grow 都爲 0,則剩餘空間保留;若當前行存在一個子容器的 flex-grow 不爲 0,則剩餘空間會被 flex-grow 不爲 0 的子容器佔據
    • 當 flex-wrap 爲 nowrap,且子容器寬度和不及父容器寬度時,flex-grow 會起做用,子容器會根據 flex-grow 設定的值放大(爲 0 的項不放大)
    • 當 flex-wrap 爲 nowrap,且子容器寬度和超過父容器寬度時,flex-shrink 會起做用,子容器會根據 flex-shrink 設定的值進行縮小(爲 0 的項不縮小)。但這裏有個較爲特殊狀況,就是當這一行全部子容器 flex-shrink 都爲 0時,即全部的子項都不能縮小,就會出現討厭的橫向滾動條
    • 總結上面四點,可看出無論在什麼狀況下,在同一時間 flex-shrink 和 flex-grow 只有一個能起做用,這其中的道理也很淺顯:空間足夠時 flex-grow 就有發揮的餘地,而空間不足時 flex-shrink 就能起做用。固然 flex-wrap 的值爲 wrap | wrap-reverse 時代表能夠換行,既然能夠換行,通常狀況下空間就老是足夠的,flex-shrink 固然就不會起做用

總結

flexbox 佈局最適合應用程序的組件和小規模佈局(一維佈局),而 Grid 佈局則適用於更大規模的佈局(二維佈局)

grid 佈局

  • grid 佈局又稱爲「網格佈局」,能夠實現二維佈局,和以前的 table 佈局差很少,同時還能夠依賴於媒體查詢根據不一樣的上下文新定義佈局(flex 佈局雖然強大,可是隻能是一維佈局)
  • 和 table 佈局不一樣的是,grid 佈局不須要在 HTML 中使用特定的標籤佈局,全部的佈局都是在 CSS 中完成的,能夠隨意定義 grid 網格
  • 網格佈局還能夠擺脫如今佈局中存在的文檔流限制,即結構不須要根據設計稿從上往下佈置了,這也意味着能夠自由地更改頁面元素位置,這最適合在不一樣的斷點位置實現最須要的佈局,而再也不須要爲響應設計而擔憂 HTML 結構的問題

沒有 HTML 結構的網格佈局有助於使用流體、調整順序等技術管理或更改佈局。經過結合 CSS 的媒體查詢屬性,能夠控制網格佈局容器和它們的子元素,使用頁面的佈局根據不一樣的設備和可用空間調整元素的顯示風格與定位,而不須要去改變文檔結構的本質內容

瀏覽器兼容性

具體可查閱:Can I use

image.png

grid 中一些概念

具體請看 CSS Grid佈局:什麼是網格佈局

網格線(Grid Lines)

  • 網格線組成了網格,是網格的水平和垂直的分界線,一個網格線存在行或列的兩側,能夠引用它的數目或定義的網格線名稱

image.png

網格軌道(Grid Track)

  • 網格軌道就是相鄰兩條網格線之間的空間,就比如表格中行或列,全部在網格中的分爲 grid column 和 grid row,每一個網格軌道可設置一個大小,用來控制寬度或高度

image.png

網格單元格(Grid Cell)

  • 網格單元格是指四條網格線之間的空間,因此它是最小的單位,就像表格中的單元格

image.png

網格區域(Grid Area)

  • 網格區域是由任意四條網格線組成的空間,因此可能包含一個或多個單元格,至關於表格中的合併單元格以後的區域

image.png

使用 grid 佈局

  • 使用 grid 佈局很簡單,經過 display 屬性設置屬性值爲 grid 或 inline-grid 或 subgrid(該元素父元素爲網格,繼承父元素的行和列的大小)
  • 網格容器中的全部子元素就會自動變成網格項目(grid item),而後設置列(grid-template-columns)和 行(grid-template-rows)的大小,設置 grid-template-columns 有多少個參數生成的 grid 列表就有多少個列,若沒有設置 grid-template-columns,那默認只有一列,寬度爲父元素的 100%
<div class="grid-container">
    <div class="item item1">1</div>
    <div class="item item2">2</div>
    <div class="item item3">3</div>
    <div class="item item4">4</div>
    <div class="item item5">5</div>
    <div class="item item6">6</div>
</div>

// 不設置 grid-template-columns,只設置 grid-template-row
.grid-container{
     display: grid;
     grid-template-rows: 50px 80px 100px;
     background: pink;
}
.item{
     border: 2px solid palegoldenrod;
     color: #fff;
     text-align: center;
     font-size: 20px;
}
複製代碼

image.png

注:當元素設置了網格佈局,column、float、clear、vertical-align 屬性無效

  • 設置了 grid-template-columns 幾個參數,就有幾列(不超過 grid item 個數),設置的 grid-template-row 參數就是每一列的高度(超出列數的高度無效),雖然設置了四個 grid-template-rows,但由於只有兩行,因此只有前兩個值生效
.grid-container {
      padding: 20px;
      display: grid;
      grid-template-rows: 50px 100px 60px 80px;
      grid-template-columns: 50px 40px 100px 80px;
      background: pink;
}
.item {
      border: 2px solid palegoldenrod;
     color: #fff;
}
複製代碼

image.png

  • 能夠像 flex 同樣設置每一列的寬度,使用了一個新的單位:fr

    CSS fr 單位是一個自適應單位,fr 單位被用於在一系列長度值中分配剩餘空間,若已指定了多個部分,則剩下的空間根據各自的數字按比例分配
    fr 是基於網格容器可用空間來計算的(flex 也是同樣),因此若是須要的話能夠和其餘單位混合使用

grid-template-columns: 1fr 1fr 2fr;
複製代碼

image.png

行或列最小、最大尺寸

  • minmax() 函數建立行或列的最小最大尺寸,第一個參數定義網格軌道的最小值,第二個參數定義網格軌道的最大值,可接受任何長度值也接受 auto 值,auto 值容許網格軌道基於內容的尺寸拉伸或擠壓
.grid-container {
     padding: 20px;
     display: grid;
     grid-template-rows: minmax(100px,200px) minmax(50px,200px);
     grid-template-columns: 1fr 1fr 2fr;
     background: pink;
     height: 300px;
}
複製代碼
  • 上面將第一行高度設爲 minmax(100px,200px),第二行高度設爲 minmax(50px,200px),容器總高度設爲 300px,這時每一列的高度要怎麼算呢?
    • 先判斷總高度是否小於第一列高度的最大值和第二列高度的最大值之和,若大於最大值之和,那第一列和第二列的高度都爲設置的最大值,如果小於最小值之和,那第一列和第二列的高度都爲設置的最小值
    • 而後這裏就是先用 總高度 300px - 第一列最小高度 100px - 第二列最小高度 50px = 150px
    • 第一列高度:第一列最小高度 100px + 150px/2 = 175px;
    • 第二列高度:第二列最小高度 50px + 150px/2 = 125px;

image.png

重複行或者列

  • repeat() 屬性可建立重複的網格軌道,這個適用於建立相等尺寸的網格項目和多個網格項目
  • repeat() 接受兩個參數:第一個參數定義網格軌道應該重複的次數,第二個參數定義每一個軌道的尺寸
.grid-container{
    padding: 20px;
    display: grid;
    grid-template-columns: repeat(2,100px);
    grid-template-rows: repeat(3,100px);
    background: pink;
}
複製代碼

image.png

間距

  • grid-column-gap:建立列與列之間的距離
  • grid-row-gap:行與行之間的距離
  • grid-gap: grid-row-gap 和 grid-column-gap 兩個屬性的縮寫形式
.grid-container{
    padding: 20px;
    display: grid;
    grid-template-columns: repeat(2,100px);
    grid-template-rows: repeat(3,100px);
    grid-column-gap: 50px;
    grid-row-gap: 15px;
    background: pink;
}
複製代碼

image.png

經過網格線定位 grid item

  • 能夠經過表格線行或者列來定位 grid item
  • grid-row:grid-row-start 和 grid-row-end 的簡寫形式
  • grid-column:grid-column-start 和 grid-column-end 的簡寫形式
  • 若只提供一個值,指定了 grid-row-start 和 grid-column-start 的值
  • 若提供兩個值,第一個值是 grid-row-start 或 grid-column-start 的值,第二個值是 grid-row-end 或 grid-column-end 的值,二者之間必需要用 / 隔開
grid-row: 2; 
grid-column: 3 / 4;
複製代碼
  • 這四個值可用 grid-area 縮寫,分別對應 grid-row-start、grid-column-start、grid-row-end、grid-column-end
grid-area: 2 / 2 / 3 / 3;
複製代碼
.grid-container {
    padding: 20px;
    display: grid;
    grid-template-columns: repeat(2,100px);
    grid-template-rows: repeat(3,100px);
    grid-column-gap: 50px;
    grid-row-gap: 15px;
    background: pink;
}
.item {
    border: 2px solid palegoldenrod;
    color: #fff;
    text-align: center;
    font-size: 20px;
}
.item1 {
    grid-row-start: 2;
    grid-row-end: 3;
    grid-column-start: 2;
    grid-column-end: 3;
    background: #fffa90;
    color: #000;
}
複製代碼

image.png

合併單元行與合併單元列

  • 這個和 excel 中的合併單元行/列是相同的(須要設置在 grid item 中)
  • 也可以使用 grid-row 和 grid-column 簡寫的形式,關鍵詞 span 後面緊隨數字,表示合併多少個列或行,/ 前面是從第幾行/列開始
.grid-container {
      padding: 20px;
      display: grid;
      grid-template-columns: repeat(4,100px);
      grid-template-rows: repeat(3,100px);
      grid-column-gap: 50px;
      grid-row-gap: 15px;
      background: pink;

}
.item {
      border: 2px solid palegoldenrod;
      color: #fff;
      text-align: center;
      font-size: 20px;
}
.item1 {
      grid-column-start: 1;
      grid-column-end: 3;
      grid-row-start: 2;
      grid-row-end: 4;
}

// 或
// .item1 {
// grid-row: 2 / span 3; 
// grid-column: span 2;
//}
複製代碼

image.png

自定義網格線名稱

  • 在 grid 中,是能夠自定義網格線的名稱的,而後使用定義好的網格線來進行佈局,[col1-start] 網格線名稱必定要使用 [] 括住
  • 下圖文字和輔助線是後臺添加的

image.png

經過網格區域命名和定位網格項目

什麼是網格區域

  • 網格區域(grid-area)是個邏輯空間,主要用來放置一個或多個網格單元格(Grid Cell),由四條網格線(Grid line)、網格區域每邊一條、四邊相交組織的網格軌道(Grid Track),網格區域是有四條網格線交織組成的網格空間,該空間中多是一個網格單元格也多是多個網格單元格

定義網格區域

  • 在 CSS Grid Layout 中定義網格區域有兩種方式,一種是經過網格線來定義,另外一種是經過 grid-template-areas 來定義

網格線定義網格區域

  • 首先依賴於 grid-template-columns 和 grid-template-rows 顯式定義網格線,甚至是由瀏覽器隱式建立網格線,而後經過 grid-area 屬性經過取網格線,組成網格線交織區域,則該區域就是所講的網格區域
  • 在使用 grid-area 屬性調用網格線,遵循的規則是 grid-area: row-start/ column-start / row-end / column-end

grid-template-areas 定義網格區域

  • 在 CSS Grid Layout 中還能夠經過 grid-template-areas 屬性來定義網格區域的名稱,而後須要放在對應網格區域的元素,能夠經過 grid-area 屬性來指定
  • 重複區域可使用同一個名稱來實現跨區域,另外對於空的軌道區域可使用點號 . 來表明
<div class="grid-container">
    <div class="header ">header</div>
    <div class="content ">content</div>
    <div class="sidebar ">sidebar</div>
    <div class="footer ">footer</div>
</div>
.grid-container {
    text-align: center;
    padding: 20px;
   display: grid;
   grid-column-gap: 5px;
   grid-row-gap: 5px;
   background: pink;
   grid-template-areas: "header header header header header"
                        "sidebar content content content content"
                        "footer footer footer footer footer";
   grid-template-rows: 50px 150px 50px;
   grid-template-columns: 200px 200px 200px;
}
.header { grid-area:header; background: #fff}
.content { grid-area: content; background: #fffa90}
.sidebar { grid-area: sidebar; background: #5bc0de}
.footer { grid-area: footer; background: #ffff00}
複製代碼

image.png

  • 這樣佈局有一個優勢,在不設置高度的狀況下(父容器和 grid-template-rows 的值,或 grid-template-rows 設置爲 auto 時,slider 和 content 的高度是一致的,且會根據其內的高度自適應),如

image.png

rem 佈局

什麼是 rem

  • rem 和 em 很容易混淆,其實兩個都是 CSS 的單位且也都是相對單位,先有的em 而後 CSS3 才引入的 rem,在介紹 rem 以前先來了解下 em

em 做爲 font-size 的單位時,其表明父元素的字體大小,em 做爲其餘屬性單位時,表明自身字體大小 —— MDN

  • em 可讓頁面更靈活、更健壯,比起處處寫死的 px 值,em 彷佛更有張力,改動父元素的字體大小子元素會等比例變化,這一變化彷佛預示了無限可能
  • 有些人提出用 em 來作彈性佈局頁面,但其複雜的計算讓人詬病,甚至有人專門作了個 px 和 em 的計算器,不一樣節點像素值對應的 em 值

image.png

  • em 作彈性佈局的缺點還在於牽一髮而動全身,一旦某個節點的字體大小發生變化,則其後代元素都得從新計算
  • rem

rem 做用於非根元素時,相對於根元素字體大小;rem 做用於根元素字體大小時,相對於其出初始字體大小 —— MDN

/* 做用於根元素,相對於原始大小(16px),因此 html 的 font-size 爲 32px*/
html {font-size: 2rem}

/* 做用於非根元素,相對於根元素字體大小,因此爲 64px */
p {font-size: 2rem}
複製代碼
  • rem 和 em 各有優勢,尺有所短,寸有所長,我一直以爲技術沒有什麼對錯,只有適合不適合,我的以爲 em 就是爲字體和行高而生的,有時子元素字體就應該相對於父元素,元素行高就應該相對於字體大小;而 rem 的優勢在於統一的參考系

rem 的佈局原理

rem 佈局的核心是設置好根 html 元素的 font-size,rem 佈局的本質在我看來是等比縮放,通常是基於寬度

假設將屏幕寬度平均分紅 100 份,每一份的寬度用 x 表示,x = 屏幕寬度 / 100,若將 x 做爲單位,x 前面的數值就表明屏幕寬度的百分比。若想要頁面元素隨着屏幕寬度等比變化,須要上面的 x 單位,不幸的是 CSS 中並無這樣的單位,幸運的是在 CSS 中有 rem,經過 rem 這個橋樑能夠實現神奇的 x

div { width: 50x } /* 屏幕寬度的50% */
複製代碼

若子元素設置 rem 單位的屬性,經過更改 html 元素的字體大小就可以讓子元素實際大小發生變化

html { font-size: 16px }
div { width: 2rem } /* 32px*/

html { font-size: 32px }
div { width: 2rem } /*64px*/
複製代碼

若讓 html 元素字體的大小恆等於屏幕寬度的 1/100,那 1rem 和 1x 就等價

html { fons-size: width / 100 }
div { width: 50rem } /* 50rem = 50x = 屏幕寬度的50% */
複製代碼

如何讓 html 字體大小一直等於屏幕寬度的百分之一呢?能夠經過 js 來設置,通常須要在頁面 dom ready、resize 和屏幕旋轉中設置

document.documentElement.style.fontSize = document.documentElement.clientWidth / 100 + 'px';
複製代碼

如何把 UE(UE 是用戶體驗(User Experience)的縮寫,指用戶界面功能圖) 圖中的獲取的像素單位的值,轉換爲以 rem 爲單位的值呢?公式元素寬度 / UE 圖寬度 * 100,假設 UE 圖尺寸是 640px,UE 圖中的一個元素寬度是 100px,根據公式則是 100 / 640 * 100 = 15.625

div { width: 15.625rem }
複製代碼

下面的表格是 UE 圖等比縮放下元素的寬度 image.png 下面的表格是經過元素在不一樣屏幕寬度下的計算值 image.png 上圖能夠看出 UE 圖寬度和屏幕寬度相同時兩邊得出的元素寬度是一致的

上面的計算過程有些繁瑣,能夠經過預處理的 function 來簡化過程,下面是 sass 的例子,less 相似

$ue-width: 640; /* ue圖的寬度 */
@function px2rem($px) {
    @return #{$px/$ue-width*100}rem;
}

div {
    width: px2rem(100);
}
複製代碼

其實有了 postcss 後,這個過程應該放到 postcss 中,源代碼以下

div { width: 100px2rem }

// postcss 會對 px2rem 這個單位進行處理,處理後的結果以下
p { width: 15.625rem }
複製代碼

在移動端中,通常爲了防止在高清屏幕下像素不夠用致使模糊,拿到的設計稿是 640px(iphone5 設備寬爲320 px)或 750px 的兩倍稿(iphone6 設備寬爲375 px),按照設備寬度作了兩倍的大小,那開發時在 CSS 中要設置什麼尺寸呢?如何作到一份設計稿適配到不一樣機型中?

  • 最佳方案是:在 photoshop 或其餘工具中量出某個元素、圖片、文字的尺寸,而後直接寫到代碼中,額外的適配不須要理會
    • 基於此,可以使用 SCSS 來提供一系列的基礎支持
    • 爲了便於計算,將頁面分爲 10 個塊,根據映射關係只需計算某個元素在頁面中佔了多少塊($rem),結合 html 中 font-size 的大小,就能在頁面上設置好正確的元素大小
      width: px2rem(200);
      
      /* 移動端頁面設計稿寬度 */
      $design-width: 750;
      /* 移動端頁面設計稿 dpr 基準值 */
      $design-dpr: 2;
      /* 將移動端頁面分爲 10 塊 */
      $blocks: 10;
      /* 縮放所支持的設備最小寬度 */
      $min-device-width: 320px;
      /* 縮放所支持的設備最大寬度 */
      $max-device-width: 540px;
      
      /* rem與px對應關係,1rem表明在JS中設置的html font-size值(爲一塊的寬度),$rem即爲$px對應占多少塊 $px $rem ------------- === ------------ $design-width $blocks */
      
      /* 單位px轉化爲rem */
      @function px2rem($px) {
          @return #{$px / $design-width * $blocks}rem;
      }
      
      /* 單位 rem 轉化爲 px,可用於根據rem單位快速計算原px */
      @function rem2px($rem) {
          @return #{$rem / $blocks * $design-width}px;
      }
      
      // 在對應的JS文件中
      // 在對應的JS文件中
      var docElem = document.documentElement,
          metaElem = document.querySelector('meta[name="viewport"]'),
          dpr = window.devicePixelRatio || 1,
          // 將頁面分爲 10 塊
          blocks = 10,
          // 須要限制的最小寬度
          defaultMinWidth = 320,
          // 須要限制的最大寬度
          defaultMaxWidth = 540,
          // 計算的基準值
          calcMaxWidth = 9999999;
      
      // 設置docElem字體大小
      function setFontSize() {
          var clientWidth = docElem.clientWidth;
          clientWidth = Math.max(clientWidth, defaultMinWidth * dpr)
          // 調整計算基準值
          if (calcMaxWidth === defaultMaxWidth) {
              clientWidth = Math.min(clientWidth, defaultMaxWidth * dpr);
          }
          docElem.style.fontSize = clientWidth / blocks + 'px';
      }
      setFontSize();
      window.addEventListener(window.orientationchange ? 'orientationchange' : 'resize', setFontSize, false);
      複製代碼

在 rem 佈局中廣泛採用 viewport scale 視窗縮放的方式,視窗縮放很簡單,直接將 meta 標籤中的 scale 進行更改,如 dpr 爲 3 則 scale 以下。但縮放在某些安卓設備中支持度不太好,還須要作其餘檢測(檢測了現用的一些機型,應該還不完整哈),同時將最終計算的 dpr 放到 html 中,供 css 作一些特殊適配

// 大部分dpr爲2如下的安卓機型不識別scale,需設置不縮放
if (navigator.appVersion.match(/android/gi) && dpr <= 2) {
    dpr = 1;
}

setScale(dpr);
// 企業QQ設置了 scale 後,不能徹底識別 scale(此時 clientWidth 未收到縮放的影響而翻倍),需設置不縮放
if (navigator.appVersion.match(/qq\//gi) && docElem.clientWidth <= 360) {
    dpr = 1;
    setScale(dpr);
}
docElem.setAttribute('data-dpr', dpr);
// 設置縮放
function setScale(dpr) {
    metaElem.setAttribute('content', 'initial-scale=' + 1 / dpr + ',maximum-scale=' + 1 / dpr + ',minimum-scale=' + 1 / dpr + ',user-scalable=no');
}
複製代碼

設置容器的最大最小寬度

  • 上面,隨着拉伸,內容區愈來愈大,各元素尺寸也愈來愈大,已經進行了最小寬度的處理
  • 要控制縮放的程度,關鍵有兩個點:尺寸計算基準、容器寬度(尺寸計算基準位於 meta 標籤中的 data-content-max,容器寬度位於 body 標籤中)
  • 在 JS 中進行匹配控制,需注意,由於已經進行了視窗的縮放,clientWidth 將會比設備寬度大,記得以 dpr 進行翻倍
  • 若僅僅限制計算基準值,容器不限制(將 body 標籤中的屬性去掉),就能夠實現某種流式效果(另外一種方案)
<!DOCTYPE html>
<html>
    <head>
        <title>REM佈局</title>
        <meta charset="utf-8">
        <meta lang="zh-CN">
        <meta name="viewport" data-content-max content="width=device-width,initial-scale=1,user-scalable=no">
        <link rel="stylesheet" href="./rem.css">
        <script src="./rem.js"></script>
    </head>

    <body data-content-max>
        <section class="container">

// js
// 須要限制的最小寬度
var defaultMinWidth = 320,
    // 須要限制的最大寬度
    defaultMaxWidth = 540,
    // 計算的基準值
    calcMaxWidth = 9999999;
if (metaElem.getAttribute('data-content-max') !== null) {
    calcMaxWidth = defaultMaxWidth;
}
...
// 設置docElem字體大小
function setFontSize() {
    var clientWidth = docElem.clientWidth;
    clientWidth = Math.max(clientWidth, defaultMinWidth * dpr)
    // 調整計算基準值
    if (calcMaxWidth === defaultMaxWidth) {
        clientWidth = Math.min(clientWidth, defaultMaxWidth * dpr);
    }

    docElem.style.fontSize = clientWidth / blocks + 'px';
}
// 在CSS中,簡單地調用一下,核心方法已經抽離
html {
    @include root-width();
}
/* html根的寬度定義 */
@mixin root-width() {
    body {
        @include container-min-width();

        &[data-content-max] {
            @include container-max-width();
        }
    }

    /* 某些機型雖然設備dpr大於1,但識別不了scale縮放,這裏須要從新設置最小寬度防止出現橫向滾動條 */
    &[data-dpr="1"] body {
        min-width: $min-device-width;
    }
}
/* 設置容器拉伸的最小寬度 */
@mixin container-min-width() {
    margin-right: auto;
    margin-left: auto;
    min-width: $min-device-width;

    @media (-webkit-device-pixel-ratio: 2) {
        min-width: $min-device-width * 2;
    }

    @media (-webkit-device-pixel-ratio: 3) {
        min-width: $min-device-width * 3;
    }
}
/* 設置容器拉伸的最大寬度 */
// 要注意的是,這裏的max-width也要配上dpr係數
@mixin container-max-width() {
    margin-right: auto;
    margin-left: auto;
    max-width: $max-device-width;

    @media (-webkit-device-pixel-ratio: 2) {
        max-width: $max-device-width * 2;
    }

    @media (-webkit-device-pixel-ratio: 3) {
        max-width: $max-device-width * 3;
    }
}
複製代碼

文本大小是否用 rem 單位

  • 有時不但願文本在 Retina 屏幕下變小,另外但願在大屏手機上看到更多文本,以及如今絕大多數的字體文件都自帶一些點陣尺寸,一般是 16px 和 24px,因此不但願出現 13px 和 15px 這樣的奇葩尺寸,能夠選擇使用 px 直接定義,若要求不嚴格也可直接使用 rem 單位
/* 設置字體大小,不使用rem單位, 根據dpr值分段調整 */
@mixin font-size($fontSize) {
    font-size: $fontSize / $design-dpr;

    [data-dpr="2"] & {
        font-size: $fontSize / $design-dpr * 2;
    }

    [data-dpr="3"] & {
        font-size: $fontSize / $design-dpr * 3;
    }
}
@include font-size(30px);
複製代碼

rem 不是銀彈

  • rem 是彈性佈局的一種實現方式,彈性佈局能夠算做響應式佈局的一種,但響應式佈局不是彈性佈局,彈性佈局強調等比縮放,100% 還原;響應式佈局強調不一樣屏幕要有不一樣的顯示,好比媒體查詢
  • 感受通常內容型的網站都不太適合使用 rem,由於大屏用戶能夠本身選擇是要更大字體仍是要更多內容,一旦使用了 rem 就剝奪了用戶的自由,如百度知道、百度經驗都沒有使用 rem 佈局;一些偏向 app 類、圖標類、圖片類的,如淘寶、活動頁面比較適合使用 rem,由於調大字體時並不能調大圖標的大小
  • rem 能夠作到 100% 的還原度,但同時 rem 的製做成本也更大,同時使用 rem 還有一些問題,下面一一列舉
    • 首先是字體的問題,字體大小並不能使用 rem,因此字體大小不能使用 rem;因爲設置了根元素字體的大小,會影響全部沒有設置字體大小的元素,由於字體大小是會繼承的,要每一個元素都顯示設置的字體大小不太合理
    • 能夠在 body 上作字體修正,如把 body 字體大小設置爲 16px,但若用戶本身設置了更大的字體,此時用戶的設置將失效,好比合理的方式是將其設置爲用戶的默認字體大小
    html { fons-size: width / 100 }
    body { font-size: 16px }
    複製代碼
    • 那字體的大小如何實現響應式呢?能夠經過修改 body 字體的大小來實現,同時全部設置字體大小的地方都是用 em 單位,由於只有 em 才能實現同步變化,固然不一樣屏幕字體大小相同也是很是合理和不錯的效果,須要你本身作決策
    @media screen and (min-width: 320px) {
      body {font-size: 16px}
    }
    @media screen and (min-width: 481px) and (max-width:640px) {
      body {font-size: 18px}
    }
    @media screen and (min-width: 641px) {
      body {font-size: 20px}
    }
    
    div {font-size: 1.2em}
    div a {font-size: 1.2em}
    複製代碼
    • 若是用戶在 PC 端瀏覽時頁面過寬怎麼辦?通常都會設置一個最大寬度,大於這個寬度的話頁面居中,兩邊留白。設置 body 的寬度爲 100rem,並水平居中
    var clientWidth = document.documentElement.clientWidth;
    clientWidth = clientWidth < 780 ? clientWidth : 780;
    document.documentElement.style.fontSize = clientWidth / 100 + 'px';
    
    body {
      margin: auto;
      width: 100rem
    }
    複製代碼
    • 若是用戶禁用了 js 怎麼辦?其實這種用戶真很少了,要不放棄吧。。。
    // 首先能夠添加noscript標籤提示用戶
    <noscript>開啓JavaScript,得到更好的體驗</noscript>
    
    // 給html添加一個320時的默認字體大小,保證頁面能夠顯示
    html {fons-size: 3.2px}
    
    // 若是想要更好的體驗,不如添加媒體查詢吧
    @media screen and (min-width: 320px) {
      html {font-size: 3.2px}
    }
    @media screen and (min-width: 481px) and (max-width:640px) {
      html {font-size: 4.8px}
    }
    @media screen and (min-width: 641px) {
      html {font-size: 6.4px}
    }
    複製代碼
  • rem 不是銀彈,這個世上也沒有銀彈,每一個方案都有優勢也有缺點,學會作出選擇和妥協纔是王道
  • rem 僅能作到內容的縮放,可是對於非矢量資源好比圖片放大時的失真並沒有法解決

rem 佈局方案

vm 佈局

  • CSS3 帶來了 rem 的同時也帶來了 vw 和 vh

vw:視口寬度的 1/100;vh:視口高度的 1/100 —— MDN

  • REM 佈局中用到了 JS 來動態設置 html 的 font-size,可能形成頁面的抖動
  • 可考慮比較新的 VW 佈局,無需使用 JS,雖然說在移動端 iOS 8 以上以及 Android 4.4 以上纔得到支持,不過仍是值得一用,若需兼容能夠嘗試 viewport-units-buggyfill
  • 有了 vw 徹底能夠繞過 rem 這個中介了,下面兩種方案是等價的
/* rem 方案 */
html { fons-size: width / 100 }
div { width: 15.625rem }

/* vw方案 */
div { width: 15.625vw }
複製代碼
  • 在使用彈性佈局時,通常會限制最小最大寬度,好比在 pc 端查看頁面,此時 vw 就力不從心了,vw 是根據設備寬度進行計算的,因此沒法設置容器最大最小寬度。由於除了 width 有 max-width,其餘單位都沒有,而 rem 能夠經過控制 html 根元素的 font-size 最大值,而輕鬆解決這個問題

rem + vw 佈局

  • 爲了解決純 vm 佈局不能設置最大最小寬度的問題,引入 rem
  • 經過配置 html 根元素的 font-size 爲 vw 單位,且配置最大最小的像素 px 值,在其餘 CSS 代碼中能夠直接使用 rem 做爲單位,調用超級簡單
html {
    @include root-font-size();
}
line-height: px2rem(300);
複製代碼
  • 而 scss 裏面的實現,一樣是先定義一個映射關係,將頁面寬度進行分塊(只是爲了防止值太大)
  • 這裏的 max-width 直接使用寬度值,由於使用的是 vw,視窗未縮放,而在頁面標籤(html 和 body)中,簡單地配上屬性表明是否須要限制寬度便可
  • 一樣是計算基準值和容器寬度兩個方面,若僅僅限制計算的基準值也能實現「流式效果」
html {
    @include root-font-size();
}
line-height: px2rem(300);
/* 移動端頁面設計稿寬度 */
$design-width: 750;
/* 移動端頁面設計稿dpr基準值 */
$design-dpr: 2;
/* 將移動端頁面分爲10塊 */
$blocks: 10;
/* 縮放所支持的設備最小寬度 */
$min-device-width: 320px;
/* 縮放所支持的設備最大寬度 */
$max-device-width: 540px;
/* rem與px對應關係,1rem表明html font-size值(爲一塊的寬度),$rem即爲$px對應占多少塊 $px $rem ------------- === ------------ $design-width $blocks */
/* html 根元素的 font-size 定義,簡單地將頁面分爲 $blocks 塊,方便計算 */
@mixin root-font-size() {
    font-size: 100vw / $blocks;

    body {
        @include container-min-width();
    }

    /* 最小寬度定義 */
    @media screen and (max-width: $min-device-width) {
        font-size: $min-device-width / $blocks;
    }

    /* 最大寬度定義 */
    &[data-content-max] {
        body[data-content-max] {
            @include container-max-width();
        }

        @media screen and (min-width: $max-device-width) {
            font-size: $max-device-width / $blocks;
        }
    }
}
/* 設置容器拉伸的最小寬度 */
@mixin container-min-width() {
    margin-right: auto;
    margin-left: auto;
    min-width: $min-device-width;
}
/* 設置容器拉伸的最大寬度 */
@mixin container-max-width() {
    margin-right: auto;
    margin-left: auto;
    max-width: $max-device-width;
}

<!DOCTYPE html>
<html data-content-max>
    <head>
        <title>VW-REM佈局</title>
        <meta charset="utf-8">
        <meta lang="zh-CN">
        <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
        <link rel="stylesheet" href="./vw-rem.css">
    </head>
複製代碼

對比

  • 每一個方案都能保證在不一樣機型下作到必定的適配
  • 通常來講,可用直接考慮使用 REM 佈局,因 REM 使用了 JS 動態設置 html 的 font-size
  • 但 scale 對安卓機型不太友好,要求極致的能夠選用 VW
  • 純 VW 佈局不支持設置容器最大最小寬高,如須要此功能則選用 REM + VW佈局
rem vm rem + vm
容器最小寬度 支持 不支持 支持
容器最大寬度 支持 不支持 支持
高清設備 1px 邊框 支持 支持 支持
容器固定縱橫比 支持 支持 支持
優勢 一、rem 單位兼容性好
二、支持高清設備 1px 邊框時可按以往方式直接寫 1px
一、無需引入 js
二、vw 的用法比較規範
同 vm
缺點 一、使用 js 設置 html 的 font-size
二、使用 scale 來設置 viewport 縮放,以支持高清設備中的 1px,但 scale 在安卓機中的兼容性不太好
三、rem 的用法不太標準,帶有一些 hack 的特變
一、vw 單位在老舊機型上不兼容
二、爲了支持高清設備的 1px,使用比較複雜的 scss mixin,佔用 :after 僞類,會產生較多的代碼
同 vm

使用方式

  • 常規方式是引入公共基礎代碼,而後在業務代碼中調用
  • 在 html 文件中能夠配置 data-content-max 參數來限制最大最小寬度
  • 在 scss 基礎部分還能夠自定義這幾個值(如果 REM 佈局,修改這些值還須要在 rem.js 文件中同步修改)
/* 移動端頁面設計稿寬度 */
$design-width: 750;
/* 移動端頁面設計稿dpr基準值 */
$design-dpr: 2;
/* 將移動端頁面分爲10塊 */
$blocks: 10;
/* 縮放所支持的設備最小寬度 */
$min-device-width: 320px;
/* 縮放所支持的設備最大寬度 */
$max-device-width: 540px;
複製代碼
相關文章
相關標籤/搜索