在列表展示中,經常會使用卡片的內容展示形式,為了美觀,常常要求各卡片間的間隙是一致的。

卡片內容不一樣可能高度不等,但一般來說為了整體的一致性,會限制每個卡片的寬高都相等。

本文就基於寬高一致的多個卡片,在不同屏幕大小下,每行卡片數量可能有調整,考量如何實現等間隙的布局。

點我預覽

放置一張張卡片項,為了設置間距,最常見的就是直接使用一個特定的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;

h2 {
margin: 20px;
padding-top: 20px;
font-size: 20px;
}
}

@include adjustContainerWidth(
$from: 1,
$to: 7
);

推薦閱讀:

相关文章