CSS Grid布局:从入门到放弃再到真香

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等。

比如创建一个简单的水平导航栏

html 复制代码
.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属性来定义它们的大小。

超简单的一个例子:

html 复制代码
.grid-container {
    display: grid;
    grid-template-columns: 1fr 2fr 1fr; /* 三列,第一列和第三列占一份空间,第二列占两份空间 */
    grid-template-rows: 100px auto 100px; /* 三行,第一行和第三行高度为100px,中间行自适应高 */
}
  • 这里的fr是一个特殊单位,表示网格容器中可用空间的等分数。除了fr,还可以使用像素(px)、百分比(%)等单位来定义轨道大小。

网格线(grid line)划分了网格轨道,每条网格线都有一个编号,从 1 开始。同时,也可以给网格线命名,方便在布局中引用。例如:

html 复制代码
.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属性,可将网格项放置到对应的网格区域中。例如:

html 复制代码
<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 布局案例解析

瀑布流布局

html 复制代码
<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>
css 复制代码
.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方式实现瀑布流

html 复制代码
<div class="waterfall-container" id="waterfall"></div>
css 复制代码
.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;
}
js 复制代码
  const container = document.getElementById('waterfall')
  const itemWidth = 200
  const gap = 5
  const itemCount = 50

  function 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.clientWidth
    console.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布局

草图
pgsql 复制代码
+--------------------------+
|        Header            |
+--------+-----------------+
| Sidebar|     Main        |
|        |  (cards, chart) |
+--------+-----------------+
html 复制代码
<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>
css 复制代码
.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"> 区块
相关推荐
赵大仁几秒前
深入理解 Pinia:Vue 状态管理的革新与实践
前端·javascript·vue.js
小小小小宇3 分钟前
业务项目中使用自定义Webpack 插件
前端
小小小小宇31 分钟前
前端AST 节点类型
前端
小小小小宇1 小时前
业务项目中使用自定义eslint插件
前端
babicu1231 小时前
CSS Day07
java·前端·css
小小小小宇1 小时前
业务项目使用自定义babel插件
前端
前端码虫1 小时前
JS分支和循环
开发语言·前端·javascript
GISer_Jing1 小时前
MonitorSDK_性能监控(从Web Vital性能指标、PerformanceObserver API和具体代码实现)
开发语言·前端·javascript
余厌厌厌1 小时前
墨香阁小说阅读前端项目
前端
fanged2 小时前
Angularjs-Hello
前端·javascript·angular.js