css 常见的布局模式 总结 回顾
主体布局
主体布局指在大部分情况下通用且具备统一特征的占位布局。掌握主体布局是一个前端必不可少的技能,养成看设计图就能大概规划出整体布局的前提是必须熟悉这些主体布局的特点与构造。
全屏布局
经典的全屏布局由顶部、底部和主体三部分组成,其特点为三部分左右满屏拉伸、顶部底部高度固定和主体高度自适应。该布局很常见,也是大部分Web应用主体的主流布局。通常使用header、footer和main三个标签语义化排版,main内还可插入aside侧栏或其他语义化标签。
<div class="layout">
<header></header>
<main>
</main>
<footer></footer>
</div>
.layout {
display: flex;
flex-direction: column;
height: 100%;
header, footer{
height: 50px;
background-color: cyan;
}
main{
flex: 1;
height: 100%;
}
}
顶部,底部高度固定且固定在顶部和底部,主体部分自适应
header{
height: 40px;
background-color: cyan;
position: sticky;
top: 0;
}
footer{
height: 40px;
background-color: #eee;
position: sticky;
bottom: 0;
}
多列布局
经典的两列布局和三列布局由左右两列组成和左中右三列组成,其特点为一列宽度固定、另一列宽度自适应和两列高度固定且相等和其特点为连续两列宽度固定、剩余一列宽度自适应和三列高度固定且相等。
<div class="two-column-layout">
<div class="left"></div>
<div class="right"></div>
</div>
.two-column-layout {
display: flex;
.left{
flex-basis: 100px;
background-color: cyan;
}
.right{
flex: 1;
background-color: darkcyan;
}
}
<div class="three-column-layout">
<div class="left"></div>
<div class="center"></div>
<div class="right"></div>
</div>
.three-column-layout {
display: flex;
.left{
flex-basis: 100px;
background-color: cyan;
}
.center {
flex-basis: 150px;
background-color: darkcyan;
}
.right{
flex: 1;
background-color: #678;
}
}
圣杯布局/双飞翼布局
经典的圣杯布局和双飞翼布局都是由左中右三列组成,其特点为左右两列宽度固定、中间一列宽度自适应和三列高度固定且相等。其实也是上述两列布局和三列布局的变体,整体的实现原理与上述N列布局一致,可能就是一些细节需注意。
<div class="grail-layout">
<div class="left"></div>
<div class="center"></div>
<div class="right"></div>
</div>
.grail-layout {
display: flex;
width: 400px;
height: 400px;
.left {
width: 100px;
background-color: #f66;
}
.center {
flex: 1;
background-color: #3c9;
}
.right {
width: 100px;
background-color: #66f;
}
}
均分布局
经典的均分布局由多列组成,其特点为每列宽度相等和每列高度固定且相等。总体来说也是最简单的经典布局,由于每列宽度相等,所以很易找到合适的方式处理。
<div class="average-layout">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
<div class="four"></div>
</div>
.average-layout {
display: flex;
width: 400px;
height: 400px;
div {
flex: 1;
}
}
居中布局
居中布局由父容器与若干个子容器组成,子容器在父容器中横向排列或竖向排列且呈水平居中或垂直居中。居中布局是一个很经典的问题,相信大家都会经常遇到。
<div class="center-layout">
<div></div>
</div>
.center-layout {
display: flex;
width: 400px;
height: 400px;
background-color: #f66;
div {
margin: auto;
width: 100px;
height: 100px;
background-color: #66f;
}
}
自适应布局
自适布局指相对视窗任何尺寸都能占据特定百分比的占位布局。自适布局的容器都是根据视窗尺寸计算,即使父节点或祖先节点的尺寸发生变化也不会影响自适布局的容器尺寸。
<div class="modal">
<div class="modal-wrapper"></div>
</div>
.modal {
display: flex;
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, .5);
&-wrapper {
width: 50vw;
height: 200px;
background-color: #f66;
}
}
当然使用calc()也不一定结合视窗比例单位计算。例如自适布局已知部分节点高度,不想手动计算最后节点高度但又想其填充布局剩余空间。
<ul class="selfadaption-layout">
<div class="box-1"></div>
<div class="box-2"></div>
<div class="box-3"></div>
</ul>
.selfadaption-layout {
width: 200px;
height: 567px;
.box-1 {
height: 123px;
background-color: #f66;
}
.box-2 {
height: 15%;
background-color: #3c9;
}
.box-3 {
height: calc(100% - 123px - 15%);
background-color: #09f;
}
}
横向布局
横向布局指容器内节点以水平方向排列且溢出部分被隐藏的占位布局。竖向布局很常见,声明overflow:hidden;width:xpx;height:ypx就能实现,但横向布局却不能使用类似方式实现。
<div class="horizontal-layout">
<ul>
<li>Alibaba</li>
<li>Tencent</li>
<li>Baidu</li>
<li>Jingdong</li>
<li>Ant</li>
<li>Netease</li>
<li>Meituan</li>
<li>ByteDance</li>
<li>360</li>
<li>Sina</li>
</ul>
</div>
.horizontal-layout {
overflow: hidden;
width: 300px;
height: 100px;
ul {
overflow-x: auto;
cursor: pointer;
&::-webkit-scrollbar {
height: 10px;
}
&::-webkit-scrollbar-track {
background-color: #f0f0f0;
}
&::-webkit-scrollbar-thumb {
border-radius: 5px;
background-color: #f66;
}
}
li {
overflow: hidden;
height: 90px;
background-color: #66f;
line-height: 90px;
text-align: center;
font-size: 16px;
color: #fff;
&:not(:first-child) {
margin-left: 10px;
}
}
}
使用flex布局完成上述布局
.horizontal-layout.flex {
ul {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
}
li {
flex-shrink: 0;
flex-basis: 90px;
}
}
凸显布局
凸显布局指容器内节点以同一方向排列且存在一个节点在某个方向上较突出的占位布局。该布局描述起来可能比较拗口,直接看以下效果吧,这是一个横向列表,节点从左往右排列,最右边的节点特别突出。这就是凸显布局的特征,凸显的节点可在凸显布局任意位置,上下左右,左上左下右上右下都行。
<ul class="highlight-layout">
<li>Alibaba</li>
<li>Tencent</li>
<li>Baidu</li>
<li>Jingdong</li>
<li>Ant</li>
<li>Netease</li>
</ul>
.highlight-layout {
display: flex;
align-items: center;
padding: 0 10px;
width: 600px;
height: 60px;
background-color: #3c9;
li {
padding: 0 10px;
height: 40px;
background-color: #3c9;
line-height: 40px;
font-size: 16px;
color: #fff;
}
&.left li {
&:not(:first-child) {
margin-left: 10px;
}
&:last-child {
margin-left: auto;
}
}
&.right li {
&:not(:last-child) {
margin-right: 10px;
}
&:first-child {
margin-right: auto;
}
}
&.top {
flex-direction: column;
width: 120px;
height: 400px;
li {
&:not(:first-child) {
margin-top: 10px;
}
&:last-child {
margin-top: auto;
}
}
}
&.bottom {
flex-direction: column;
width: 120px;
height: 400px;
li {
&:not(:last-child) {
margin-bottom: 10px;
}
&:first-child {
margin-bottom: auto;
}
}
}
}
间距布局
间距布局指容器内节点从左往右从上往下排列且以特定间距间隔的占位布局。间距布局常见于各大列表,是笔者认为最重要的布局之一。为何如此简单的布局还是花费一些篇幅讲解呢?最近笔者查看了Github上很多与间隔布局相关的CSS代码,虽然整体效果看上去无大碍,但margin/padding和结构选择器却乱用,因此笔者想从零到一纠正间距布局的正确编码方式。
<ul class="spacing-layout">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
</ul>
.spacing-layout {
display: flex;
overflow: auto;
flex-wrap: wrap;
margin-top: 20px;
padding: 20px;
width: 700px;
height: 400px;
background-color: #f66;
li {
width: 200px;
height: 200px;
background-color: #66f;
line-height: 200px;
text-align: center;
font-size: 20px;
color: #fff;
&:not(:nth-child(3n)) {
margin-right: 20px;
}
&:nth-child(n+4) {
margin-top: 20px;
}
}
}
空载布局
空载布局指容器内无任何节点时使用其他形式代替的占位布局。还有使用JS判断列表集合为空时显示占位符吗?相信很多使用MVVM框架开发的同学都会使用条件判断的方式渲染虚拟DOM,若列表长度不为0则渲染列表,否则渲染占位符。
<ul class="empty-layout">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
</ul>
<ul class="empty-layout"></ul>
$empty: "https://yangzw.vip/img/empty.svg";
.empty-layout {
overflow: auto;
width: 200px;
height: 150px;
outline: 1px solid #3c9;
&:empty {
display: flex;
justify-content: center;
align-items: center;
background: url($empty) no-repeat center/100px auto;
&::after {
margin-top: 90px;
font-weight: bold;
content: "没钱就没数据";
}
}
li {
padding: 0 10px;
height: 30px;
background-color: #09f;
line-height: 30px;
color: #fff;
&:nth-child(even) {
background-color: #f90;
}
}
}
多格布局
多格布局指容器内节点以动态数量的格子形式排列的占位布局。微信朋友圈的相册就是最常见的多格布局了,当单张照片排列、两张照片排列、三张照片排列等等,每种情况下照片的尺寸都可能不一致。笔者制作了一个动态多格相册怀念我家狗狗AB。大家感受下纯CSS实现动态数量的多格布局吧。
<ul class="multigrid-layout">
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
<li class="item"><img src="https://static.yangzw.vip/codepen/ab-3.jpg"></li>
</ul>
@mixin square($count: 2) {
$length: calc((100% - #{$count} * 10px) / #{$count});
width: $length;
height: $length;
}
.multigrid-layout {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
align-content: flex-start;
padding: 5px;
border: 1px solid #ccc;
border-radius: 5px;
width: 400px;
height: 400px;
li {
display: flex;
overflow: hidden;
justify-content: center;
margin: 5px;
background-color: #f0f0f0;
@include square(3);
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
// 一个元素
.item:only-child {
border-radius: 10px;
width: auto;
max-width: 80%;
height: auto;
max-height: 80%;
}
// 两个元素
.item:first-child:nth-last-child(2),
.item:first-child:nth-last-child(2) ~ .item:nth-child(2) {
@include square(2);
}
.item:first-child:nth-last-child(2) {
border-radius: 10px 0 0 10px;
}
.item:first-child:nth-last-child(2) ~ .item:nth-child(2) {
border-radius: 0 10px 10px 0;
}
// 三个元素
.item:first-child:nth-last-child(3),
.item:first-child:nth-last-child(3) ~ .item:nth-child(2),
.item:first-child:nth-last-child(3) ~ .item:nth-child(3) {
@include square(2);
}
.item:first-child:nth-last-child(3) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(3) ~ .item:nth-child(2) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(3) ~ .item:nth-child(3) {
border-bottom-left-radius: 10px;
}
// 四个元素
.item:first-child:nth-last-child(4),
.item:first-child:nth-last-child(4) ~ .item:nth-child(2),
.item:first-child:nth-last-child(4) ~ .item:nth-child(3),
.item:first-child:nth-last-child(4) ~ .item:nth-child(4) {
@include square(2);
}
.item:first-child:nth-last-child(4) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(4) ~ .item:nth-child(2) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(4) ~ .item:nth-child(3) {
border-bottom-left-radius: 10px;
}
.item:first-child:nth-last-child(4) ~ .item:nth-child(4) {
border-bottom-right-radius: 10px;
}
// 五个元素
.item:first-child:nth-last-child(5) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(5) ~ .item:nth-child(3) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(5) ~ .item:nth-child(4) {
border-bottom-left-radius: 10px;
}
// 六个元素
.item:first-child:nth-last-child(6) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(6) ~ .item:nth-child(3) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(6) ~ .item:nth-child(4) {
border-bottom-left-radius: 10px;
}
.item:first-child:nth-last-child(6) ~ .item:nth-child(6) {
border-bottom-right-radius: 10px;
}
// 七个元素
.item:first-child:nth-last-child(7) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(7) ~ .item:nth-child(3) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(7) ~ .item:nth-child(7) {
border-bottom-left-radius: 10px;
}
// 八个元素
.item:first-child:nth-last-child(8) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(8) ~ .item:nth-child(3) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(8) ~ .item:nth-child(7) {
border-bottom-left-radius: 10px;
}
// 九个元素
.item:first-child:nth-last-child(9) {
border-top-left-radius: 10px;
}
.item:first-child:nth-last-child(9) ~ .item:nth-child(3) {
border-top-right-radius: 10px;
}
.item:first-child:nth-last-child(9) ~ .item:nth-child(7) {
border-bottom-left-radius: 10px;
}
.item:first-child:nth-last-child(9) ~ .item:nth-child(9) {
border-bottom-right-radius: 10px;
}