Flexbox 與 Grid 布局:基礎概念與特點
Flexbox
Flexbox(Flexible Box Layout),即彈性盒布局模型,主要用于創建一維布局,能夠輕松實現元素在一行或一列中的排列、對齊與分布。通過display: flex屬性啟用 Flexbox 布局后,容器成為彈性容器,子元素成為彈性項。
其核心概念包括主軸(main axis)和交叉軸(cross axis)。
- 開發者可以通過justify-content屬性控制彈性項在主軸上的對齊方式,如flex-start(默認值,左對齊或上對齊)、flex-end(右對齊或下對齊)、center(居中對齊)、space-between(兩端對齊,項目之間的間隔都相等)、space-around(每個項目兩側的間隔相等,所以項目之間的間隔比項目與邊框的間隔大一倍)等;
- 使用align-items屬性控制彈性項在交叉軸上的對齊方式,取值如stretch(默認值,拉伸以填充容器)、flex-start、flex-end、center等。
比如創建一個簡單的水平導航欄
.nav {display: flex;// justify-content: space-around;align-items: center;gap: 10px;
}.nav-item {
/* 導航項樣式 */
}
Grid
Grid 布局是一個基于網格的二維布局系統,專門用于創建復雜的網格結構,能夠同時在行和列兩個維度上精確控制元素的位置與大小。通過display: grid屬性將容器定義為網格容器,容器內的子元素成為網格項。
Grid 布局引入網格軌道的概念,其指的是網格中的行或列;可以通過grid-template-rows和grid-template-columns屬性來定義它們的大小。
超簡單的一個例子:
.grid-container {display: grid;grid-template-columns: 1fr 2fr 1fr; /* 三列,第一列和第三列占一份空間,第二列占兩份空間 */grid-template-rows: 100px auto 100px; /* 三行,第一行和第三行高度為100px,中間行自適應高 */
}
- 這里的fr是一個特殊單位,表示網格容器中可用空間的等分數。除了fr,還可以使用像素(px)、百分比(%)等單位來定義軌道大小。
網格線(grid line)劃分了網格軌道,每條網格線都有一個編號,從 1 開始。同時,也可以給網格線命名,方便在布局中引用。例如:
.grid-container {display: grid;grid-template-columns: [col1-start] 1fr [col2-start] 2fr [col2-end] 1fr [grid-end];grid-template-rows: [row1-start] 100px [row2-start] auto [row3-start] 100px [grid-end];
}
.item1 {grid-column: col1-start / col2-end; /* 從第一列開始,到第二列結束 */grid-row: row1-start / row2-start; /* 第一行 */
}.item2 {grid-column: col2-start / grid-end; /* 從第二列開始,到最后 */grid-row: row2-start / row3-start; /* 第二行 */
}
網格區域(grid area)是由四條網格線圍成的矩形區域,可以通過grid-template-areas屬性來定義。結合grid-area屬性,可將網格項放置到對應的網格區域中。例如:
<div class="grid-container"><div class="header">Header</div><div class="header1">Header1</div><div class="header2">Header2</div><div class="main1">Main1</div><div class="main2">Main2</div><div class="sidebar">Sidebar</div><div class="footer">Footer</div><div class="footer1">Footer1</div><div class="footer2">Footer2</div>
</div>.grid-container {display: grid;grid-template-areas: "header header1 header2""main1 main2 sidebar""footer footer1 footer2";grid-template-columns: 2fr 1fr 1fr;grid-template-rows: 100px auto 100px;gap: 10px;
}.header { grid-area: header; background: #d0e; }
.header1 { grid-area: header1; background: #cce; }
.header2 { grid-area: header2; background: #ace; }
.main1 { grid-area: main1; background: #eda; }
.main2 { grid-area: main2; background: #ebf; }
.sidebar { grid-area: sidebar; background: #bee; }
.footer { grid-area: footer; background: #ffc; }
.footer1 { grid-area: footer1; background: #ffa; }
.footer2 { grid-area: footer2; background: #eef; }
Grid 布局案例解析
瀑布流布局
<div class="waterfall-grid"><div class="waterfall-item"><img src="https://picsum.photos/200/300?random=1" alt="Image 1"></div><div class="waterfall-item"><img src="https://placehold.co/200x300?text=2" alt="Image 2"></div><div class="waterfall-item"><img src="https://loremflickr.com/320/240?lock=3" alt="Image 3"></div><div class="waterfall-item"><img src="https://picsum.photos/300/300?random=4" alt="Image 4"></div><div class="waterfall-item"><img src="https://placehold.co/200x300?text=5" alt="Image 5"></div><div class="waterfall-item"><img src="https://loremflickr.com/320/240?lock=6" alt="Image 6"></div><div class="waterfall-item"><img src="https://picsum.photos/200/300?random=7" alt="Image 7"></div><div class="waterfall-item"><img src="https://placehold.co/200x300?text=8" alt="Image 8"></div><div class="waterfall-item"><img src="https://loremflickr.com/320/240?lock=9" alt="Image 9"></div><div class="waterfall-item"><img src="https://picsum.photos/200/300?random=12" alt="Image 10"></div>
</div>
.waterfall-grid{display: grid;grid-template-columns: repeat(4, 1fr);grid-gap: 10px;grid-template-rows: masonry;
}
.waterfall-item{width: 100%;display: block;
}
- 僅適用于firefox瀏覽器!!
js+css方式實現瀑布流
<div class="waterfall-container" id="waterfall"></div>
.waterfall-container {position: relative;width: 100%;
}.waterfall-item {position: absolute;width: 200px;transition: all 0.3s ease;box-shadow: 0 2px 10px rgba(0,0,0,0.1);border-radius: 8px;overflow: hidden;
}
.waterfall-item img {width: 100%;display: block;
}
const container = document.getElementById('waterfall')const itemWidth = 200const gap = 5const itemCount = 50function createImage(index) {const div = document.createElement('div')div.className = 'waterfall-item'const height = 150 + Math.floor(Math.random() * 150) // 隨機高度div.innerHTML = `<img src="https://picsum.photos/200/${height}?random=${index}" alt="img">`return div}function layoutWaterfall() {const containerWidth = container.clientWidthconsole.log(containerWidth, container,'containerWidth')const columns = Math.floor(containerWidth / (itemWidth + gap))const columnHeights = Array(columns).fill(0)const items = Array.from(container.children)items.forEach((item, index) => {const minCol = columnHeights.indexOf(Math.min(...columnHeights))const x = minCol * (itemWidth + gap)const y = columnHeights[minCol]item.style.left = x + 'px'item.style.top = y + 'px'columnHeights[minCol] += item.offsetHeight + gap})container.style.height = Math.max(...columnHeights) + 'px'}// 初始化渲染for (let i = 0; i < itemCount; i++) {const item = createImage(i)container.appendChild(item)}// 等圖片加載后再布局window.addEventListener('load', layoutWaterfall)window.addEventListener('resize', () => {setTimeout(layoutWaterfall, 200)})
- 主要邏輯就是每次找到最短的一列,將新加入的圖片放入該列
Dashboard布局
草圖
+--------------------------+
| Header |
+--------+-----------------+
| Sidebar| Main |
| | (cards, chart) |
+--------+-----------------+
<div class="dashboard-grid"><header class="header">Header</header><aside class="sidebar">Sidebar</aside><main class="main"><div class="card">Card 1</div><div class="card">Card 2</div><div class="card">Card 3</div><div class="card">Card 4</div></main>
</div>
.dashboard-grid {display: grid;grid-template-areas:"header header""sidebar main";grid-template-columns: 250px 1fr;grid-template-rows: 60px 1fr;height: 100vh;
}.header {grid-area: header;background: #2d3e50;color: white;padding: 1rem;
}.sidebar {grid-area: sidebar;background: #34495e;color: white;padding: 1rem;
}.main {grid-area: main;padding: 1rem;display: grid;grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));gap: 16px;
}.card {background: white;padding: 1rem;border-radius: 8px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
- 可以隨意的擴充圖表,加入
<div class="chart">
區塊