grid实现复杂响应式布局

133 阅读5分钟

背景

如果要实现下面的布局效果 image.png

使用flexbox

  • 把1,2合并成左边一个新,再和3 做flex的column布局。
  • 1,2在新的盒子4里也做flex 设置row布局

image.png

我用flexbox写一下代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>测试</title>
    <style>
        html,body {
            margin: 0;
            padding: 0;
        }
        .container {
            width: 400px;
            display: flex;
            flex-direction: column;
        }
        .item { 
            /* border: 1px solid #000; */
            height: 50px;
            text-align: center; 
            padding: 10px;    
            box-sizing: border-box;
        } 
        .flexbox {
            width: 100%;
            display: flex; 
            flex-direction: row;
            margin-bottom: 10px;
        }
        .box1 {
            background-color: gray;
            width: 50%;
            margin-right: 10px;
        }
        .box2 {
            background-color: green;
            width: 50%;
        }
        .box3 {
            background-color: gold;
            width: 100%;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="flexbox">
            <div class="item box1">1</div>
            <div class="item box2">2</div>
        </div> 

       <div class="item box3">3</div> 
    </div>
</body>
</html>

image.png

缺点:

  • 只能实现一维的布局,嵌套的关系开始变得复杂
  • 所有的边距都需要单独设置
  • 平行的两个元素,由于没有关联性,很难控制一样的自适应宽度

如果只是一次性工作还好,最怕有一天产品跟你说移动端的时候需要做适配。

要响应式变成下面效果

image.png

用已有的结构flexbox需要单独调整原有的结构,并且所有已经设置的边距由于物理位置发生变化也需要同步调整

如果 使用flexbox 就要再次嵌套

image.png

  • 把1,3合并成左边一个新,再和2 做flex的row布局。(注意是1和3,不是1和2)
  • 1,2在新的盒子4里也做flex 设置column布局

这还是比较简单的,如果设置的pc是这样

image.png

移动端是这样,那该如何是好呢?

image.png

这时候grid就有他用武之地。

grid出场

我们以上面最后复杂的网格用grid实现一下,pc的布局

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>测试</title>
    <style>
        html,body {
            margin: 0;
            padding: 0;
        }
        .container {
            margin: 10px;
            display: grid;
            grid-template-areas:
                'box1 box1 box1'
                'box2 box3 box4'
                'box5 box6 box6 '
                'box7 box7 box7';
                padding: 0;
                grid-row-gap: 0.24em; 
                grid-column-gap: 0.24em;
        } 
        .item {
            text-align: center; 
            padding: 10px;    
            box-sizing: border-box;
        }  
        .box1 {
            grid-area: box1;
            background-color: gray; 
        }
        .box2 {
            grid-area: box2;
            background-color: green; 
        }
        .box3 {
            grid-area: box3;
            background-color: gold;
        }
        .box4 {
            grid-area: box4;
            background-color: rgb(0, 200, 255);
        }
        .box5 {
            grid-area: box5;
            background-color: rgb(255, 145, 0);
        }
        .box6 {
            grid-area: box6;
            background-color: rgb(174, 0, 255);
        }
        .box7 {
            grid-area: box7;
            background-color: rgb(163, 33, 122);
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="box1 item" >
            1
        </div>
        <div class="box2 item" >
            2
        </div>
        <div class="box3 item" >
            3
        </div>
        <div class="box4 item" >
           4 
        </div>
        <div class="box5 item" >
            5
        </div>
        <div class="box6 item" >
            6
        </div>
        <div class="box7 item" >
            7
        </div>
    </div>
</body>
</html>

可以看到代码非常清晰,由于grid天生就是一个二维结构,描述整个网格非常直观。并且可以自己命名网格名,语意化强。

执行效果: image.png

即时是移动的 也只需要调整一下 grid-template-areas的排列参数即可

<style>
@media (max-width: 767px) {
   .container { 
    grid-template-areas:
        'box1 box1 box1'
        'box2 box3 box4'
        'box5 box6 box6 '
        'box7 box7 box7'; 
    } 
}
</style>

显示效果

image.png

注意

实际开发具体的宽度/高度 可以使用rem或 vh/vw 设置不同格子的宽度/高度即可,以及 aspect-ratio: 宽/高,设置更准确的比例宽高。

grid 常用概念

开启grid模式

.container {
  display: grid; 
}

固定宽高

.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  grid-template-rows: 100px 100px 100px;
}

固定比例

.container {
  display: grid;
  grid-template-columns: 33.33% 33.33% 33.33%;
  grid-template-rows: 33.33% 33.33% 33.33%;
}

使用函数固定比例

.container {
  display: grid;
  grid-template-columns: repeat(3, 33.33%);
  grid-template-rows: repeat(3, 33.33%);
}

fr 关键字 等价于flex 的1 的概念,会根据实际设置的两个值做等比计算

.container {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

grid-row-gap 设置行间隙 grid-column-gap 设置列间隙

.container {
    grid-row-gap: 10px; 
    grid-column-gap: 10px; 
}

auto 自动适应宽度

grid-template-columns: 100px auto 100px;

grid-auto-flow

默认的放置顺序是"先行后列",即先填满第一行,再开始放入第二行

grid-auto-flow: row;

先列后行

grid-auto-flow: column;

dense 尽量排满,把其他地方满足的往里面填充,顺序无法控制,适合瀑布流效果。

grid-auto-flow: row dense;

水平 ,垂直 对齐,跟flex类似,place-content 水平 ,垂直 的合并

.container {
  justify-content: start | end | center | stretch | space-around | space-between | space-evenly;
  align-content: start | end | center | stretch | space-around | space-between | space-evenly;  
  place-content: start | end | center | stretch | space-around | space-between | space-evenly;  
}

grid-template-areas

是我觉得最有用的属性,当前提前把 grid-area 指定后并填充相关的html,就可以在grid-template-areas 的参数里使用.

如上面例子中,我们只要把grid-area提前设置好

    .box1 {
        grid-area: box1;
    }
    .box2 {
        grid-area: box2;
    }
    .box3 {
        grid-area: box3;
    }
    .box4 {
        grid-area: box4;
    }
    .box5 {
        grid-area: box5;
    }
    .box6 {
        grid-area: box6;
    }
    .box7 {
        grid-area: box7;
    }

html的内容也必须要有7个

 <div class="container">
    <div class="box1" >
        1
    </div>
    <div class="box2" >
        2
    </div>
    <div class="box3" >
        3
    </div>
    <div class="box4" >
       4 
    </div>
    <div class="box5" >
        5
    </div>
    <div class="box6" >
        6
    </div>
    <div class="box7" >
        7
    </div>
</div>

下面就可以开始排列

    .container {
        display: grid;
        grid-template-areas:
            'box1 box1 box1'
            'box2 box3 box4'
            'box5 box6 box6 '
            'box7 box7 box7';
    } 

注意排列的内容不能多,也不能少,如果你需要更加精致的划分,建议不要用33,可以改成66 ,这样能够更加准确的控制,下面这个设置跟上面是等价的。

    .container {
        display: grid;
        grid-template-areas:
            'box1 box1 box1 box1 box1 box1'
            'box2 box2 box3 box3 box4 box4'
            'box5 box5 box6 box6 box6 box6'
            'box7 box7 box7 box7 box7 box7';
    } 

不需设置的空间 使用.

grid-template-areas: 'a . c'
                     'd . f'
                     'g . i';