(譯)關於響應式的另一種思考

原文

原作者

一說到響應式設計,肯定離不開媒體查詢 (Media Query) ,它可以改變頁面的布局以精確適應不同的設備。媒體查詢通常是我們在實現響應式設計時最後考慮的方案,在使用媒體查詢之前,我們總是會多思考一下:還有沒有其他的實現方法呢?下文就由此思考,總結了一些技巧來實現簡單的響應式布局,或許展示的效果並不是很完美,但希望這些解決思路會為大家帶來一些新的啟發。

四大金剛

「四大金剛」是筆者從 The Fab Four Technique 轉譯過來的,最初在 Rémi Parmentier 的博客中被提到, 初衷是幫助回複電子郵件,通過延伸發現它可以很容易地用於普通網頁,為創建自適應網頁帶來了新的方法,如 Thierry 所示。 例如,利用「四大金剛」中的四個關鍵屬性 width , min-width , max-width 和 calc 創建一個基於寬度的響應式效果:

{n min-width: 50%;n width: calc((25em - 100%) * 1000);n max-width: 100%;n}n

外部容器用百分比控制其寬度,然後 calc 函數將該值與所設定的斷點(這裡設置斷點為25em)進行比較,然後得到一個正數(如果寬度小於斷點),或者一個負數(如果寬度大於斷點),或者正好是零。當為正數時,容器的寬度由 max-width 限制,當為負數或零寬度時容器寬度由 min-width 限制。

所以,在上面的例子中,我們設置一個斷點為 25em 。如果 font-size 為 16px ,則容器的斷點被解析為 400px 。如果容器是 400px 或更大(換句話說大於或等於斷點),寬度將解析為零的或一個大的負數:

計算過程:(400-400 = 0)* 1000 = 0 (400-401 = -1)* 1000 = -1000

因此,當容器寬度大於 400px 時,min-width 就會發揮作用,所以上例中的元素的總共寬度將為50%。當容器是 399px 或更低(換句話說小於斷點),寬度解析為一個大的正數:

計算過程:(400-399 = 1)* 1000 = 1000

在這種情況下,max-width 的最終寬度為 100% 。下圖應該有助於可視化這個過程:

接下來的四個演示都使用這種技術切換元素的寬度以實現響應式。

浮動圖像的全寬和半寬

我們經常會需要實現圖片在大屏幕上佔比 50% ,小屏幕圖片佔比 100% 的效果,如下圖所示,那麼如果不用媒體查詢需要怎麼做呢?

  • 大屏幕情況

  • 小屏幕情況

關鍵代碼:

<article class="float-module">n...n <div class="float-module__image float-module__image--left">n <img src="..." />n </div>n...n <p>...</p>n</article>n

.float-module__image {n min-width: 50%;n width: calc((25em - 100%) * 1000);n max-width: 100%; n margin-bottom: 1rem;n}n.float-module__image img {n width: 100%;n}n.float-module__image--left {n float: left;n margin-right: 1rem;n}n.float-module::after {n content: "";n display: table;n clear: both;n}n

這裡是完整例子

浮動圖像的顯示和隱藏

以往我們在實現響應式時,為了在小屏幕突出視覺重點,會隱藏一些在大屏幕上才會顯示的元素,這會讓我們不得不多寫一些CSS 樣式去實現該效果。我們可以利用 calc 來實現在小屏幕時隱藏圖片。顯示效果如下圖:

  • 大屏幕情況

  • 小屏幕情況

關鍵代碼:

<article class="float-hidden-module">n <div class="float-hidden-module__image float-hidden-module__image--left">n <img src="..." />n </div>n <p>...</p>n ...n</article>n

.float-hidden-module:after {n content: "";n display: table;n clear: both;n}n.float-hidden-module__image {n width: calc((25em - 100%) * -1000);n max-width: 50%; n margin-bottom: 1rem;n overflow: hidden;n}n.float-hidden-module__image img {nn /* 圖片與文本之間創建空隙 */n width: calc(100% - 1em);n}n.float-hidden-module__image--left {n float: left;n}n

當屏幕寬度大於 400px 時,.float-hidden-module__image 的 width > 0,因此 max-width 生效,該容器的寬度為 50%,當屏幕寬度小於或等於 400px 時,.float-hidden-module__image 的 width < = 0,該容器寬度為零,此時圖片被隱藏。

為了清楚起見,更簡短的展示:

{n /* 移除 min-width ,因為我們希望寬度 < = 0 時都被視為 0 */n width: calc((25em - 100%) * -1000);n max-width: 100%;n}n

這裡是完整例子

文字和圖片的覆蓋和堆疊

很多時候,我們會需要在全屏顯示的圖片上添加文字描述,當屏幕寬度縮小,圖片上不足以放下文字的時候,文字置於圖片下方顯示,顯示效果如下圖:

  • 大屏幕情況

  • 小屏幕情況

關鍵代碼:

<div class="overlay-stacked-module">n <div class="overlay-stacked-module__image">n <img src="..." />n </div>n <div class="overlay-stacked-module__pull"></div>n <div class="overlay-stacked-module__body">n ...n <p>n ...n </p>n </div>n</div>n

$module-breakpoint: 30em;n$primary-color: rgb(230,206,137);n$secondary-color: rgb(171, 101, 20);nn.overlay-stacked-module {n /* 如果 pull 容器的高度 > module_body 容器內容的高度,那麼它就會被擠到這個容器的下方,因此需要隱藏溢出*/n overflow: hidden;n}nn.overlay-stacked-module__image {n position: relative;n}nn.overlay-stacked-module__image::after {n content: "";n display: block;n position: absolute;n left: 0;n top: 0;nn /* 額外的 .5% 是為了彌補四捨五入帶來的損失 */n height: 100.5%;nn /* 設置漸變,使文字更加清晰 */n background-image: linear-gradient(to bottom, rgba($primary-color,0) 0%,rgba($primary-color,0) 50%,rgba($primary-color,1) 100%);n 當屏幕寬度大於斷點時,max-width 生效n width: calc((#{$module-breakpoint} - 100%) * -1000);n max-width: 100%;n}nn.overlay-stacked-module__image img {n width: 100%;n}nn.overlay-stacked-module__pull {n /* 文字浮於圖片上方 */n margin-bottom: -10em;nn max-height: 10em;nn overflow: hidden;n}n.overlay-stacked-module__pull::before {n content: "";n display: block;n padding-bottom: calc((#{$module-breakpoint} - 100%) * 1000);n}n/* padding 是基於寬度的,所以我們可以有一個基於寬度的斷點,將其與容器寬度(100%)進行比較,並將設置比較大的正負 padding 。當寬度為 0 時,負的 padding 無效,因此,實際上我們基於容器的 padding > = 0。*/n.overlay-stacked-module__body {n padding: 1em;n /* 保證 body 容器出現在圖片之上 */n position: relative;n}n

和之前的做法有些相似,用另一個 div 將文本覆蓋到圖片上,如果圖像太小,圖片就會被文字遮擋,或者文字在圖片下面。這部分技術實現有些複雜,慢慢分析一下:

.pull {n /* 將文字浮於圖片之上 */n margin-bottom: -10em;n}n

這裡,負邊距將下面的文字向上拉起,使得它覆蓋圖像。 但是,當容器寬度超過斷點時,我們需要將文字恢復正常,避免文字將圖像完全遮擋掉,但是由於沒有 min / max-margin 屬性,因此不能使用「四大金剛」來實現這一點。可以換另外一種想法,如果給 padding 一個百分比,在百分比的容器中, padding-top 和 padding-bottom 會影響元素的高度。 有了這個想法,就可以使用 calc 創建一個 padding-bottom 值,這樣容器的寬度可以在 「零」 或 「非常大」 之間進行切換:

padding-bottom: calc((30em - 100%) * 1000);n

我們不能直接應用 在 .pull 這個 div 上 ,因為沒有 min / max-padding 屬性來限制這些值,解決方案是將 padding 放在偽元素上以強制更改高度,並在 .pull 元素上使用 max-height 將高度限制為與負邊距 (margin) 相同的值,以抵消該邊距。

.pull {n /* 文字浮於圖片之上 */n margin-bottom: -10em;n max-height: 10em;nn /* 為了美觀,隱藏溢出部分 */n overflow: hidden;n}nn.pull::before {n content: "";n display: block;n padding-bottom: calc((30em - 100%) * 1000);n}n

漸變疊加效果是通過如前面所述向已應用的背景漸變偽元素添加一個 on/off 開關來實現的:

.image::after {n content: "";n display: block;n position: absolute;n left: 0;n top: 0;n /* 額外的 .5% 是為了彌補四捨五入帶來的損失 */n height: 100.5%;n max-width: 100%;n}n

這裡是完整例子

導航欄項目的顯示和隱藏

導航欄的所有項目在大屏幕時依次排開顯示,當屏幕寬度逐漸變小,最右側的項目逐漸被隱藏,並在最右方出現一個 「更多」按鈕,點擊該按鈕能夠展開被摺疊隱藏的項目。不用媒體查詢,依然使用「四大金剛」,只不過這裡是基於容器的高度,而不是寬度。顯示效果:

  • 大屏幕下顯示:

  • 小屏幕下顯示:

  • 小屏幕下點擊「more」:

關鍵代碼:

<div class="outer">n <div class="inner">n <div class="item">...</div>n ...n <div class="control">...</div>n </div>n</div>n...n

.outer {n height: 2.25em;n overflow: hidden;n}n/* 點擊外部容器時,高度自動,以展示被隱藏的項目*/n.outer:target {n height: auto;n}n

外部容器具有固定的高度,並且隱藏任何溢出,除非元素是 :target 狀態。

.inner {n display: flex;n flex-wrap: wrap;n}n

內部容器是一個彈性容器,用 flex-wrap 實現換行,所以容器的高度隨著元素內部的高度增加而增加,但第一行下面的元素將被 .outer 容器的 overflow:hidden 隱藏,從而出現截斷效果。

.control {n height: calc((2.25em - 100%) * -1000);n max-height: 2.25em;n}nn.outer:target .control--open {n display: none;n}nn.outer:target .control--close {n display: block;n}n

more / less 控制項只有在容器的高度超過斷點時可見,並且 :target 狀態可以控制哪個控制項是可見的,這樣子我們就簡單實現了一個隨著屏幕寬度切換而展示不同效果的導航欄。

這裡是完整例子

小結

上面幾種方法實現了簡單的響應式部分需求,但是很多情況下,如果沒有媒體查詢就不能實現。 例如顏色值,字體大小和線高度,邊框和框陰影等等。但是如果不用媒體查詢就能實現自己需要的響應式,豈不是更加美好(?????????)。


推薦閱讀:

2017 年了,這麼多前端框架,你會怎樣選擇?
精讀《最佳前端面試題》及面試官技巧
前端工具鏈課(二)—— 模塊化工具及組件化思想
使用Nuxt.js改善現有項目

TAG:CSS | 响应式设计Responsivewebdesign | 前端开发 |