在列表展示中,經常會使用卡片的內容展示形式,為了美觀,常常要求各卡片間的間隙是一致的。
卡片內容不一樣可能高度不等,但一般來說為了整體的一致性,會限制每個卡片的寬高都相等。
本文就基於寬高一致的多個卡片,在不同屏幕大小下,每行卡片數量可能有調整,考量如何實現等間隙的布局。
點我預覽
放置一張張卡片項,為了設置間距,最常見的就是直接使用一個特定的margin值了,這種方式雖然可以(通過精確計算後確實也可以)
直接設置一個間距,比如統一 margin-left 和 margin-bottom都為 20px ,並不能保證每行最後一個卡片之後的間距是20px
關於如何定這個 margin的值,需要通過一個規則來計算,這個後文再說明
設置同等間距,常用的還有 flex布局中的 justify-content: space-between,可以定義各子項目以相同間距布局,但不好處理左右子項目與邊框的間距。 space-around這個就更用不得了,會使得左右子項目右margin == 左margin * 2
所以最終還是回到使用margin值來設置,通過一個可用的規則,來保證間距是一致的。
先把基本結構搭上
<div class="container"> <h2>項目列表</h2> <ul class="proj-items"></ul> </div>
<!-- 模板結構 --> <script type="text/template" id="proj-item-tpl"> <li class="proj-item"> <a href="#/p/{{projectID}}"> <h3 class="proj-item__title">{{projectName}}</h3> <p class="proj-item__author">{{author}}</p> </a> </li> </script>
JS生成N個項目
function addEvent(elem, type, handler) { elem.addEventListener(type, handler, false); }
function qs(selector) { return document.querySelector(selector); }
function qsa(selectors) { return document.querySelectorAll(selectors); }
var mockData = (function(num) { var data = [];
for (var i = 1; i <= num; ++i) { data.push({ projectID: i, projectName: 項目 + i, author: 張大大 }); }
return data; })(8);
var itemTpl = qs(#proj-item-tpl).innerHTML; var itemsDOM = qs(.proj-items);
/** * 渲染數據 * @param {[type]} data [description] * @return {[type]} [description] */ function renderList(data) { var html = ; var fragment = document.createDocumentFragment();
data.forEach(function(item) { var divTemp = document.createElement(div);
// 模板替換 divTemp.innerHTML = itemTpl.replace(/{{(w+)}}/g, function(input, match) { return match ? item[match] || : ; });
fragment.appendChild(divTemp.firstElementChild); });
// 渲染 itemsDOM.appendChild(fragment); }
renderList(mockData);
把基礎樣式放上,這裡我們先指定一個特定的itemMargin值為20px
$itemMargin: 20px; $itemWidth: 130px; $itemHeight: 150px;
.container { margin: 20px auto; width: 450px; background-color: #f2f2f2; color: #666;
h2 { margin: 20px; padding-top: 20px; font-size: 20px; } }
.proj-items { display: flex; flex-wrap: wrap; /* justify-content: space-between; */ padding: 0; list-style: none;
&:after { content: ""; display: block; flex-grow: 99999; } }
.proj-item { margin-left: $itemMargin; margin-bottom: $itemMargin; width: $itemWidth; height: $itemHeight; background-color: #fff; border-radius: 3px; text-align: center;
&:hover { box-shadow: 0 0 20px #ddd; }
a { display: block; padding: 15px; height: 100%; color: #666; text-decoration: none; }
&__title { margin-top: 0; font-size: 16px; }
&__author { font-size: 12px; } }
可以看到,每行最後一個間距不一致了,所以不能簡單的寫個margin值
再來看看設置 space-between的時候
.proj-items { justify-content: space-between; ... }
.proj-item { /* margin-left: $itemMargin; */ margin-bottom: $itemMargin; ... }
看來並不夠強大
如果看得仔細,應該能看到項目7和8是挨在一起的,為何沒有間距呢
其一是因為沒有margin-left值,其二是在項目列表後放了一個坑來佔位,防止最後一行項目過少時 space-between的值太大了
把這個撤掉看看這個影響
&:after { content: ""; display: block; flex-grow: 99999; }
還是把目光投向margin值的設定規則吧
在設計一個頁面布局時,至少已經確定了XX頁面大小的情況下,容器寬度應該設置為多少(比如為1200px),每行放n個項目,項目的寬高是多少
有了這些指標(也必須有這些指標),我們就可以用來計算margin值了
containerWidth == n * itemWidth + (n + 1) * itemMargin
得出
itemMargin = (containerWidth - n * itemWidth) / (n + 1)
代入這裡的情況,containerWidth 450px,itemWidth 130px,每行 3個,即可得出 itemMargin 正好為 15px
有了某種特定情況下的布局規則之後,接下來還要考慮不同屏幕大小的情況下,怎麼調整這個margin值
這個需要結合媒體查詢來設定,同時相應的計算規則也可以通過scss來處理
第一種情況是每行3個,n只可能為整數,即可推算出需要處理的臨界值為1 2 3 4 5 6 ... 這些整數值
加入n為4,如果要保證 itemMargin值15px在各種情況下都相等,計算可得 容器寬度containerWidth值 為 595px
同理求得 n是5時為 740px ,n是2時為 305px
當然,如果覺得這個containerWidth值不太好看,也可以自己定義,比如 n是4的時候設置為 600px,代入公式那麼 itemMargin值為16px。
為了保證各種請下間距都相等,我個人就不推薦這麼幹了
通過上述的規則計算,我們可以得出每行項目數量遞增時的容器寬度臨界值
把這些臨界值放在媒體查詢裡面配置,即可方便地實現這種布局的響應式處理
主要的處理為
/* 這兩個為初始就確定的基準值 */ $containerWidth: 305px; $itemMargin: 15px;
$itemWidth: 130px; $itemHeight: 150px;
/* 每行項目數量為itemNum時的容器寬度 */ @function getContainerWidth($itemNum) { @return $itemNum * $itemWidth + ($itemNum + 1) * $itemMargin; }
/* 配置各個頁面寬度下的容器寬度(應用) */ @mixin adjustContainerWidth( $from: 2, $to: 5 ) { @for $i from $from through $to { $minWidth: getContainerWidth($i); $maxWidth: getContainerWidth($i + 1);
@media only screen and (min-width: $minWidth) and (max-width: $maxWidth) { .container { width: $minWidth; } } } }
.container { margin: 20px auto; width: $containerWidth; background-color: #f2f2f2; color: #666;
@include adjustContainerWidth( $from: 1, $to: 7 );
推薦閱讀: