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"> 区块
相关推荐
顾林海6 分钟前
Flutter 图标和按钮组件
android·开发语言·前端·flutter·面试
雯0609~26 分钟前
js:循环查询数组对象中的某一项的值是否为空
开发语言·前端·javascript
bingbingyihao32 分钟前
个人博客系统
前端·javascript·vue.js
尘寰ya33 分钟前
前端面试-HTML5与CSS3
前端·面试·css3·html5
最新信息35 分钟前
PHP与HTML配合搭建网站指南
前端
前端开发张小七1 小时前
每日一练:3统计数组中相等且可以被整除的数对
前端·python
天天扭码1 小时前
一杯咖啡的时间吃透一道算法题——2.两数相加(使用链表)
前端·javascript·算法
Hello.Reader1 小时前
在 Web 中调试 Rust-Generated WebAssembly
前端·rust·wasm
NetX行者1 小时前
详解正则表达式中的?:、?= 、 ?! 、?<=、?<!
开发语言·前端·javascript·正则表达式
流云一号1 小时前
Python实现贪吃蛇三
开发语言·前端·python