前端常见布局总结

155 阅读9分钟
等高布局

让两列不同高度的容器一样高,高度不够的部分自己撑起来。

<div class="main">
    <div>
        <p>测试内容</p>
        <p>测试内容</p>
        <p>测试内容</p>
        <p>测试内容</p>
    </div>
    <div>
        <p>测试内容</p>
        <p>测试内容</p>
        <p>测试内容</p>
        <p>测试内容</p>
        <p>测试内容</p>
        <p>测试内容</p>
    </div>
</div>

可以使用浮动:

.main{
    width:500px;
    background:skyblue;
    overflow: hidden;
}
.main div{
    width:100px;
    background:pink;
    float:left;
}
.main div:nth-of-type(2){
    float:right;
}

这里给父元素设置overflow: hidden,是为了触发BFC,来让父元素包裹浮动子元素。关于BFC在这里总结过。

如果用flex就更为简洁了:

.main{
    width:500px;
    background:skyblue;
    display: flex;
    justify-content: space-between;
}
.main div{
    width:100px;
    background:pink;
} 
两列与三列布局

这种布局就是两边两列固定宽度,中间一列自适应。

<div class="main">
    <div class="col1"></div>
    <div class="col2"></div>
    <div class="col3"></div>
</div>

用flex非常简单,给中间一列设置flex-grow: 1占满剩余空间即可,关于flex布局详情,可以看阮一峰大佬的博客

.main{
    height:100vh;
    background:skyblue;
    display: flex;
}
.col1{
    width:200px;
    background:pink;
}
.col2{
    flex-grow: 1;
    background:springgreen;
}
.col3{
    width:100px;
    background: tomato;
} 
Sticky Footer布局

粘性页脚布局,当内容区高度不够时,页脚固定在底部,当内容变长时,页脚也会向下移动。

<div class="header"></div>
<div class="content">
    <p>测试内容</p>
    <p>测试内容</p>
    <p>测试内容</p>
    <p>测试内容</p>
    <p>测试内容</p>
</div>
<div class="footer"></div>
.main{
    min-height:100vh;
    display: flex;
    flex-direction: column;
}
.main .header{
    height:100px;
    background:pink;
}
.main .content{
    flex-grow: 1;
}
.main .footer{
    height:100px;
    background:skyblue;
}

这也是利用flex布局,给中间的内容区域设置flex-grow: 1。

栅格布局

这里先对grid先做一个简单的总结,详细看阮一峰大佬博客。grid布局和flex类似,都有容器和项目的概念,flex布局是轴线布局,只能指定项目针对轴线的位置,属于一维布局;grid将容器分为行和列,产生单元格,然后指定项目所在的单元格,属于二维布局。

  • 容器属性

    • grid-template-columns 定义每一列的列宽
    • grid-template-rows 定义每一行的行高
    • auto-fill关键字 容器尽可能多的容纳项目
    • grid-row-gap 设置行与行的间隔
    • grid-column-gap 设置列的间隔
    • grid-template-areas 定义区域
    • justify-content 内容区域水平排列(左中右)
    • align-content 内容区域垂直排列(左中右)
  • 项目属性

    这四个属性主要指定项目的四个边框来确定项目的位置,

    • grid-column-start 左边框所在的垂直网格线
  • grid-column-end 右边框所在的垂直网格线

    • grid-row-start 上边框所在的垂直网格线
  • grid-row-end 下边框所在的垂直网格线

    • grid-area 指定项目在哪一个区域

接下来用上述属性做一个简单的栅格布局:

<div class="row">
    <div class="col-6">1</div>
    <div class="col-3">2</div>
    <div class="col-4">3</div>
    <div class="col-5">4</div>
</div>
.row{
    background:skyblue;
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    grid-template-rows: 50px;
    grid-auto-rows: 50px;
}
.row div{
    background:pink;
    border:1px black solid;
}
.row .col-1{
    grid-area: auto/auto/auto/span 1;
}
.row .col-2{
    grid-area: auto/auto/auto/span 2;
}
.row .col-3{
    grid-area: auto/auto/auto/span 3;
}
.row .col-4{
    grid-area: auto/auto/auto/span 4;
}
.row .col-5{
    grid-area: auto/auto/auto/span 5;
}
.row .col-6{
    grid-area: auto/auto/auto/span 6;
}
.row .col-7{
    grid-area: auto/auto/auto/span 7;
}
.row .col-8{
    grid-area: auto/auto/auto/span 8;
}
.row .col-9{
    grid-area: auto/auto/auto/span 9;
}
.row .col-10{
    grid-area: auto/auto/auto/span 10;
}
.row .col-11{
    grid-area: auto/auto/auto/span 11;
}
.row .col-12{
    grid-area: auto/auto/auto/span 12;
}

这里设置了12列,每一行高50px,每一个项目设置不同的类名,占据不同的栅格。

容器自适应行列布局

表格展示时,行或者列自适应展示:

<div class="main">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
</div>
.main{ 
  height:300px;
  background:skyblue;
  display: inline-grid;
  grid-template-rows: repeat(3, 1fr);
  grid-auto-flow: column;
  grid-auto-columns: 100px;
  gap:5px;
}
.main div{
  background:pink;
}

接下来利用grid布局做一个小案例,

<div class="main">
    <div>a1</div>
    <div>a2</div>
    <div>a3</div>
    <div>a4</div>
    <div>a5</div>
    <div>a6</div>
    <div>a7</div>
</div>
.main{ 
    width:300px;
    height: 400px;
    margin: 0 auto;
    background:skyblue;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(4, 1fr);
    grid-template-areas: 
    "a1 a3 a3"
    "a2 a3 a3"
    "a4 a4 a5"
    "a6 a7 a7";
    gap:5px;
}
.main div{
    background:pink;
}
.main div:nth-of-type(1){
    grid-area: a1;
}
.main div:nth-of-type(2){
    grid-area: a2;
}
.main div:nth-of-type(3){
    grid-area: a3;
}
.main div:nth-of-type(4){
    grid-area: a4;
}
.main div:nth-of-type(5){
    grid-area: a5;
}
.main div:nth-of-type(6){
    grid-area: a6;
}
.main div:nth-of-type(7){
    grid-area: a7;
}
移动端适配:像素、rem和vw

有这样三个概念要熟悉下:

  • 物理像素:设备屏幕实际拥有的像素点。比如iPhone 6的屏幕在宽度方向有750个像素点,高度方向有1334个像素点,所以iPhone 6 总共有750*1334个物理像素
  • 逻辑像素: 也叫“设备独立像素”(Device Independent Pixel, DIP),可以理解为反映在CSS/JS代码里的像素点数。
  • 设备像素比: 一个设备的物理像素与逻辑像素之比

以常用的iPhone6为例,它的物理像素就是750X1334,而它的逻辑像素就是375X667,它的物理像素比就是2。

在移动端不同的屏幕尺寸时,需要将页面等比方法或者等比缩小,就需要动态的计算像素。em就是根据font-size来设置计算大小的,rem,rem的值就是根据根元素html的font-size来计算的,可以据此来动态的计算body的font-size来,动态计算根节点的font-size,比如设置1rem=100px,有两种,

  • js动态计算

    let width = document.documentElement.clientWidth;
    document.documentElement.style.fontsize = 100 * (width / 640) + 'px'
    
  • vw动态计算

    vw就是页面将可视区的宽度和高度分划分为100vw和100vh,如果在375像素的屏幕下(iphone6下),设置成1rem为100px,可以这样计算:100*100/375 = 26.67

同样也可以使用vscode插件,比如px to rem或者px to vw来设置编辑器也可以。

响应式布局

实现响应式布局,就要用到媒体查询。详细的api可以查看mdn。

其常用的就是定位媒体特性,给定user agent输出设备或环境的特定特征,也就是定义一个约束范围,达到条件时就渲染指定的css样式。

@media (max-width: 300px) { ... }

只有当浏览器宽度小于等于300px时,css样式才会生效。

media可以结合栅格布局,很好的实现响应式栅格系统,接下来实现一个简易的栅格系统:

<div class="row">
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-1</div>
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-2</div>
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-3</div>
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-4</div>
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-5</div>
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-6</div>
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-7</div>
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-8</div>
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-9</div>
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-10</div>
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-11</div>
    <div class="col-xxl-3 col-lg-4 col-sm-6">col-12</div>
</div>
.row{
    background:skyblue;
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    grid-template-rows: 50px;
    grid-auto-rows: 50px;
}
.row div{
    background:pink;
    border:1px black solid;
    grid-area: auto/auto/auto/span 12;
}
.row .col-1{
    grid-area: auto/auto/auto/span 1;
}
.row .col-2{
    grid-area: auto/auto/auto/span 2;
}
.row .col-3{
    grid-area: auto/auto/auto/span 3;
}
.row .col-4{
    grid-area: auto/auto/auto/span 4;
}
.row .col-5{
    grid-area: auto/auto/auto/span 5;
}
.row .col-6{
    grid-area: auto/auto/auto/span 6;
}
.row .col-7{
    grid-area: auto/auto/auto/span 7;
}
.row .col-8{
    grid-area: auto/auto/auto/span 8;
}
.row .col-9{
    grid-area: auto/auto/auto/span 9;
}
.row .col-10{
    grid-area: auto/auto/auto/span 10;
}
.row .col-11{
    grid-area: auto/auto/auto/span 11;
}
.row .col-12{
    grid-area: auto/auto/auto/span 12;
}

@media (min-width: 576px){
    .row .col-sm-1{
        grid-area: auto/auto/auto/span 1;
    }
    .row .col-sm-2{
        grid-area: auto/auto/auto/span 2;
    }
    .row .col-sm-3{
        grid-area: auto/auto/auto/span 3;
    }
    .row .col-sm-4{
        grid-area: auto/auto/auto/span 4;
    }
    .row .col-sm-5{
        grid-area: auto/auto/auto/span 5;
    }
    .row .col-sm-6{
        grid-area: auto/auto/auto/span 6;
    }
    .row .col-sm-7{
        grid-area: auto/auto/auto/span 7;
    }
    .row .col-sm-8{
        grid-area: auto/auto/auto/span 8;
    }
    .row .col-sm-9{
        grid-area: auto/auto/auto/span 9;
    }
    .row .col-sm-10{
        grid-area: auto/auto/auto/span 10;
    }
    .row .col-sm-11{
        grid-area: auto/auto/auto/span 11;
    }
    .row .col-sm-12{
        grid-area: auto/auto/auto/span 12;
    }
}

@media (min-width: 768px){
    .row .col-md-1{
        grid-area: auto/auto/auto/span 1;
    }
    .row .col-md-2{
        grid-area: auto/auto/auto/span 2;
    }
    .row .col-md-3{
        grid-area: auto/auto/auto/span 3;
    }
    .row .col-md-4{
        grid-area: auto/auto/auto/span 4;
    }
    .row .col-md-5{
        grid-area: auto/auto/auto/span 5;
    }
    .row .col-md-6{
        grid-area: auto/auto/auto/span 6;
    }
    .row .col-md-7{
        grid-area: auto/auto/auto/span 7;
    }
    .row .col-md-8{
        grid-area: auto/auto/auto/span 8;
    }
    .row .col-md-9{
        grid-area: auto/auto/auto/span 9;
    }
    .row .col-md-10{
        grid-area: auto/auto/auto/span 10;
    }
    .row .col-md-11{
        grid-area: auto/auto/auto/span 11;
    }
    .row .col-md-12{
        grid-area: auto/auto/auto/span 12;
    }
}

@media (min-width: 992px){
    .row .col-lg-1{
        grid-area: auto/auto/auto/span 1;
    }
    .row .col-lg-2{
        grid-area: auto/auto/auto/span 2;
    }
    .row .col-lg-3{
        grid-area: auto/auto/auto/span 3;
    }
    .row .col-lg-4{
        grid-area: auto/auto/auto/span 4;
    }
    .row .col-lg-5{
        grid-area: auto/auto/auto/span 5;
    }
    .row .col-lg-6{
        grid-area: auto/auto/auto/span 6;
    }
    .row .col-lg-7{
        grid-area: auto/auto/auto/span 7;
    }
    .row .col-lg-8{
        grid-area: auto/auto/auto/span 8;
    }
    .row .col-lg-9{
        grid-area: auto/auto/auto/span 9;
    }
    .row .col-lg-10{
        grid-area: auto/auto/auto/span 10;
    }
    .row .col-lg-11{
        grid-area: auto/auto/auto/span 11;
    }
    .row .col-lg-12{
        grid-area: auto/auto/auto/span 12;
    }
}
瀑布流布局
  • 横向瀑布流

使用flex布局来实现:

<div class="main">
    <div class="item">
        <img src="./imgs/1.jpg" alt="">
    </div>
    <div class="item">
        <img src="./imgs/2.jpg" alt="">
    </div>
    <div class="item">
        <img src="./imgs/3.jpg" alt="">
    </div>
    <div class="item">
        <img src="./imgs/4.jpg" alt="">
    </div>
    <div class="item">
        <img src="./imgs/5.jpg" alt="">
    </div>
    <div class="item">
        <img src="./imgs/6.jpg" alt="">
    </div>
</div>
.main{
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}
.main .item{
    flex-grow: 1;
    height: 200px;
    /* flex-basis: 200px; */
}
.main .item img{
    width:100%;
    height:100%;
    object-fit: cover;
    display: block;
}

这里再给每个图片动态设置一个flexBasis:

var items = document.querySelectorAll('.item');
for(var i=0;i<items.length;i++){
    items[i].style.flexBasis = Math.random() * 200 + 200 + 'px';
}
  • 竖向瀑布流

    • Multi-Columns 多栏布局

      **CSS多列布局(CSS Multi-column Layout)**是一种定义了多栏布局的模块,可支持在布局中建立列(column)的数量,以及内容如何在列之间流动(flow)、列之间的间距(gap)大小,以及列的分隔线(column rules)。

      详细可以查看MDN,这种比js动态计算要简单的多。

      .main{
          /* column-count: 4; */
          column-width: 300px;
          column-gap: 20px;
          column-rule: 1px gray dashed;
      }
      h1{
          column-span: all;
          text-align: center;
      }
      .main .item img{
          width:100%;
          height:100%;
          object-fit: cover;
          display: block;
      }
      
    • js动态计算

      <div class="container">       
      </div>
      <button class="more-btn">加载更多</button>
      
      .container{
          display: flex;
          gap: 10px;
          align-items: flex-start;
      }
      .item-col{
          flex-basis: 300px;
          flex-grow: 1;
      }
      .item-box{
          padding-top:10px;
          break-inside: avoid;
      }
      .item-box img{
          width:100%;
          height:100%;
          object-fit: cover;
          display: block;
      }
      .more-btn{
          display: block;
          margin: 30px auto;
          padding: 20px;
          font-size: 30px;
      }
      
      var container = document.querySelector('.container');
      var itemBox = document.getElementsByClassName('item-box');
      var moreBtn = document.querySelector('.more-btn');
      var now = 0;
      var cols = Math.floor(window.innerWidth / 300);
      
      for(var i=0;i<cols;i++){
          var itemCol = document.createElement('div');
          itemCol.className = 'item-col';
          container.append(itemCol);
      }
      
      (function(){
          var _arg = arguments;
          if(now === 15){
              return;
          }
          now++;
          var itemBox = document.createElement('div');
          var itemImg = document.createElement('img');
          itemBox.className = 'item-box';
          itemImg.src = `./imgs/${now}.jpg`;
          itemBox.append(itemImg);
          itemImg.onload = function(){
              minCols().append(itemBox);
              _arg.callee();     
          };
      })();
      
      function minCols(){
          var itemCols = document.querySelectorAll('.item-col');
          var arr = [...itemCols];
          arr.sort(function(c1,c2){
              return c1.offsetHeight - c2.offsetHeight;
          });
          return arr[0];
      }
      
      window.onresize = function(){
          var changeCols = Math.floor(window.innerWidth / 300);
          if( changeCols === cols ){
              return;
          }
          cols = changeCols;
          var itemBoxs = document.querySelectorAll('.item-box');
          container.innerHTML = '';
          for(var i=0;i<changeCols;i++){
              var itemCol = document.createElement('div');
              itemCol.className = 'item-col';
              container.append(itemCol);
          }
          var itemCols = document.querySelectorAll('.item-col');
        
          for(var i=0;i<itemBoxs.length;i++){
              minCols().append(itemBoxs[i]);  
          }
      };
      
      moreBtn.onclick = function(){
          var itemCols = document.querySelectorAll('.item-col');
          var now = 14;
          
          (function(){
              var _arg = arguments;
              if(now === 20){
                  return;
              }
              now++;
              var itemBox = document.createElement('div');
              var itemImg = document.createElement('img');
              itemBox.className = 'item-box';
              itemImg.src = `./imgs/${now}.jpg`;
              itemBox.append(itemImg);
              itemImg.onload = function(){
                  minCols().append(itemBox);
                  _arg.callee();     
              };
          })();
      
      };
      

      这里首先根据屏幕的宽度来动态计算列数,然后计算最高度低的列来放照片。核心代码就是minCols函数,他拿到所有的列,然后用offsetHeight来进行一个排序,然后返回高度最低的一列,将创建的图片放到这一列,然后通过_arg.callee()不断的重复这一过程。