1、傳列數,根據列數計算元素容器寬度
好處是子元素可以寫百分比寬度,不用固定某一種寬度,反正知道列數通過計算間距就能得到外層容器的寬度。
舉個簡單的例子:
(ps:以下用例皆在html中去模擬,就不另外起react或者vue應用了,主打一個輕便。)
在瀑布流下面的柵格布局
- 先嘗試固定列數3,并且固定寬高100px
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><style>.item {color: white;background-color: violet;width: 100px;height: 100px;margin: 2px;text-align: center;}.waterfall {display: flex;flex-wrap: wrap;}</style></head><body><div class="grid waterfall"></div></body><script>const column = 3;const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,];const UIList = list.map((item) => {return `<div class="item">${item}</div>`;});const renderList = new Array(column);const finalList = UIList.forEach((item, childIndex) => {const realIndex = childIndex % column;if (!renderList[realIndex]) {renderList[realIndex] = [];}renderList[realIndex].push(item);console.log("renderList", renderList);});const html = renderList.map((item) => {return `<div class="waterfall-item">${item.join("")}</div>`;});document.querySelector(".grid").innerHTML = html.join("");</script>
</html>
得到的結果:
好,寫法ok,讓我們試試列數固定(通過api傳入),子元素寬度根據公式計算呢。
理論計算公式:元素寬度 = (屏幕寬度 - 左右間距寬度 - 列之間的間距)/ 列數
注意3列的話,計算列的寬度就是 2個列寬,自己體會。
好!理論成立,實踐開始~!
我們修改下css,讓item元素的寬高不在固定。
.item {color: white;background-color: violet;height: auto;margin: 2px;text-align: center;}
于是我們得到了上面一坨,因為寬度和高度根據內容自適應了,所以只包裹了填充的數字。
嗯,在意料之中。
那么關鍵的計算來了,且看下面的js:
const column = 3; // 列數const screenWith = window.innerWidth; // 屏寬const paddingLeft = 12; // 頁面左間距const paddingRight = 12; // 頁面右間距const columnWith = 8; // 列之間的間距
我們先設定頁面左右間距為12(px),列間距為8(px).
然后我們就可以計算單個元素容器寬度了:
// 四舍五入并且精確到2位小數,防止列數過多而誤差過大const itemWidth =Math.round(((screenWith - paddingLeft - paddingRight - columnWith * (column - 1)) /column) *100) / 100;
然后將元素的間距設置成和js里寫的一致(當然你也可以把他們開放成動態的api):
完整demo:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><style>* {margin: 0;padding: 0;}.item {color: white;background-color: violet;height: 50px;margin-bottom: 8px;text-align: center;}.item:last-child {margin-bottom: 0;}.waterfall {display: flex;flex-wrap: wrap;padding: 0 12px;}.waterfall-item {margin-left: 8px;}.waterfall-item:first-child {margin-left: 0;}</style></head><body><div class="grid waterfall"></div></body><script>const column = 3;const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,];const screenWidth = window.innerWidth;const paddingLeft = 12;const paddingRight = 12;const columnWidth = 8;const itemWidth =Math.round(((screenWidth - paddingLeft - paddingRight - columnWidth * (column - 1)) /column) *100) / 100;console.log("itemWidth", itemWidth);const UIList = list.map((item) => {return `<div class="item" style="width: ${itemWidth}px;">${item}</div>`;});const renderList = new Array(column);const finalList = UIList.forEach((item, childIndex) => {const realIndex = childIndex % column;if (!renderList[realIndex]) {renderList[realIndex] = [];}renderList[realIndex].push(item);});const html = renderList.map((item) => {return `<div class="waterfall-item">${item.join("")}</div>`;});document.querySelector(".grid").innerHTML = html.join("");</script>
</html>
效果:
測試高度不一樣的柵格效果
增加修改以下代碼,意思是給5的倍數的子元素高度設置到100px
.more-height {height: 100px;}const UIList = list.map((item, index) => {return `<div class="item ${index % 5 === 0 ? "more-height" : ""}" style="width: ${itemWidth}px;">${item}</div>`;});
效果:
不錯,符合預期~
如果我們連列數colum都不想傳了咋辦?能不能自適應屏幕尺寸設置列數
答案當然是肯定的,前提你要和設計師商量好什么尺寸下出多少列。
比如我們規定:
320px以下,展示2列
320~600px, 展示3列
600~920px,展示4列
920px以上,還是4列,但是容器寬度增加,這時候就需要子元素也做了響應式,否則會造成容器空曠很多。
使用以下方法,動態確定colum列數:
let column = 2;function handleResize() {const screenWidth = window.innerWidth;if (screenWidth < 320) {column = 2;} else if (screenWidth >= 320 && screenWidth < 600) {column = 3;} else {column = 4;}}// 監聽窗口大小變化window.addEventListener("resize", handleResize);// 頁面加載時執行一次屏幕尺寸判斷handleResize();
看看效果:
320以下展示2行:
320~600 展示3行:
600~920 展示4行:
920往上還是4行,但是單元格寬度增加:
上面的唯一缺點大概是因為是js控制,頁面只要不刷新,即使尺寸變了也不會去動態的計算列數,在移動端影響不大~
2、根據子元素寬度,自動適應列數
如果是采用這種方案,那么子元素必須顯示的定義了寬度,即只有子元素存在定義的寬度時才能正確計算。
這種方法則對子元素要求很高,需要子元素定寬并且適配了各種各樣屏幕寬度下的尺寸。
我們的計算規則則變成了:
列數 = (屏幕寬度 - 頁面左右間距- (列數-1) *列間距) / 子元素寬度(固定)
該公式解出來變成:
列數 = (屏寬 - 左右間距 + 列間距) / (子元素寬度+列間距)
假定我們各項數據值是:
const screenWith = window.innerWidth;const paddingLeft = 12; // 左間距const paddingRight = 12; // 右間距const columnWidth = 8; // 列寬const itemWidth = 120; // 子元素寬度
計算規則如下:
// 列數必須向下取整,否則會放不下column = Math.floor((screenWith - paddingLeft - paddingRight + columnWidth) /(itemWidth + columnWidth));
像上面這種就是剩余寬度明顯不夠一列被向下取整給截斷了。
那么有沒有很好的解法呢,目前看來如果子元素定寬,不太好自適應的。
非要定寬可以考慮:
1、列間距自適應
2、整體布局居中
另外在設置子元素寬度時
計算失敗的幾種情況:
1、子元素定義了100%寬度,旨在根據容器寬度去定寬,但是容器也沒有定寬。
2、子元素沒有設置寬度導致計算失敗,這時候會寫一個兼容值,那么固定就會獲取這個兼容值。
以上就是個人碎碎念~
希望有所收獲!