阳光里她在院子中央晾晒着衣裳 / 在四季的风中她散着头发安慰着时光css
——赵雷《南方姑娘》前端
响应式布局系统,在如今流行的 CSS 框架中已经很是常见了。它主要由容器类和约定一行列数的栅格系统组成,组成了一个框架的骨架。bootstrap
在流行的前端框架 Bootstrap 和 Bulma CSS 中,就有体现。像 Bootstrap 的 .container
、.row
、.col
;还有 Bulma CSS 的 .container
、columns
、column
都是表示这类布局系统。虽然名称不同,但原理都是相同的。前端框架
随着 Flex 布局的普及,几乎现代的栅格系统的实现都选择使用这一灵活的布局方式。数据结构
如今就来看一下,怎样实现一个最小的 CSS 响应式布局系统吧。框架
首先从容器提及。ide
为了保证明现代码的简洁,本文将使用 SCSS 来写。若是你对 SCSS 还不熟悉,没有关系,行文中会对使用到的知识点作介绍。布局
容器主要用来包裹网页的主要内容,常见效果就是将内容居中地显示在屏幕中间。flex
咱们使用 .container
来约定容器。spa
首先,容器是水平居中的,这一块样式较为容易:
.container {
margin-left: auto;
margin-right: auto;
}
复制代码
所谓的响应式容器,就是根据不一样的断点(breakpoints),也就是当前的视口宽度,来决定容器使用的 max-width
值。
这里咱们借鉴了 Bootstrap 中对断点的定义,根据视口宽度,分为如下几类设备:
[0, 576px)
[576px, 768px)
[768px, 992px)
[992px, 1200px)
[1200px, +∞)
针对断点定义,声明一个变量 $breakpoints
:
$breakpoints: (
// Extra small screen / phone
xs: 0,
// Small screen / phone
sm: 576px,
// Medium screen / tablet
md: 768px,
// Large screen / desktop
lg: 992px,
// Extra large screen / wide desktop
xl: 1200px
);
复制代码
$breakpoints
称为“列表”,是 SCSS 提供给咱们的数据结构。由一个个 key: value
键值对组成。上例中的 key 表示的是设备有效范围的起始点。
不一样的设备下,容器有不一样的 max-width
值。因此,这里咱们再声明一个表示容器宽度的变量 $container-max-widths
:
$container-max-widths: (
xs: none,
sm: 540px,
md: 720px,
lg: 960px,
xl: 1140px
);
复制代码
这里的 $container-max-widths
也是个列表,这里的 key 表示某个设备下容器的最大宽度。好比,在超大屏设备下,容器的最大宽度是 1140px
,而在日常手机下,不设置容器的最大宽度,为默认值 none
。
有了实现的思路,接下来就着手实现。
咱们就能够借助媒体查询指令 @media
,依据视口宽度的范围,给予 .container
不一样的 max-width
值。
@each $device, $breakpoint in $breakpoints {
@media only screen and (min-width: $breakpoint) {
.container {
max-width: map-get($container-max-widths, $device);
}
}
}
复制代码
7 行代码搞定!
下面解释下上面的代码。
咱们对列表遍历,使用的是 @each...in
语法,每一次遍历取出对应的 key、value,获得当前的 $device
、$breakpoint
。map-get
是 SCSS 提供的用来操做列表的方法:根据 key 取出 value。好比,当 $device
值为 xs
的时候,map-get($container-max-widths, $device)
对应值为 none
;当 $device
值为 sm
的时候,map-get($container-max-widths, $device)
对应值为 540px
,以此类推。
@media only screen and (min-width: $breakpoint) { ... }
中包含的代码,表示从当前设备断点开始处,应用的 CSS 样式。当咱们同时按照从小到大的顺序设置两个断点的媒体查询时,后者会覆盖前者的样式,这是实现不一样视口下,具备不一样宽度容器的核心原理。
接下来,将获得的宽度值赋给容器的 max-width
属性就能够了。
到如今为止,咱们就写出了一个响应式容器了,咱们总揽下代码:
$breakpoints: (
// Extra small screen / phone
xs: 0,
// Small screen / phone
sm: 576px,
// Medium screen / tablet
md: 768px,
// Large screen / desktop
lg: 992px,
// Extra large screen / wide desktop
xl: 1200px
);
$container-max-widths: (
xs: none,
sm: 540px,
md: 720px,
lg: 960px,
xl: 1140px
);
.container {
margin-left: auto;
margin-right: auto;
}
@each $device, $breakpoint in $breakpoints {
@media only screen and (min-width: $breakpoint) {
.container {
max-width: map-get($container-max-widths, $device);
}
}
}
复制代码
点击这里,查看效果。
下面再来介绍 12 列栅格布局。
先使用 Flex 布局,写一个最简的等宽布局。
.row {
display: flex;
.col {
flex-grow: 1;
flex-basis: 0;
}
}
复制代码
没错,这就是使用 Flex 布局实现一个等宽布局的全部代码了。若是不考虑中间的空白行,只须要 7 行代码。
这里的原理是,咱们将全部 Flex 项目的 flex-basis
设置为 0
了,就是说这些 Flex 项目在 grow 或 shrink 以前都没有宽度,是同样长的。这样最终计算出来的主轴空间会平均地分配给了每一个 Flex 项目,这样它们就等宽了。
到这里,咱们所写的这个简易栅格布局有两个局限:
换行的话很好弄,为 Flex 容器加个 flex-wrap: wrap
就能够了。那怎样处理“非等宽项目”排列布局呢。
为了能实现非等宽项目的布局,咱们的思路是:禁用 Flex 项目的伸缩特性,使用百分比 width
指定宽度。
首先,禁用 Flex 项目的伸缩特性,使用到的属性以下:
flex-shrink: 0;
flex-grow: 0;
flex-basis: 0;
复制代码
这三个属性等价的快捷写法是:
flex: none;
复制代码
而后就是使用百分比 width
指定宽度了。
咱们实现的是一行最多 12 列的栅格布局。也就是说把一行划分红 12 列,每一列的宽度大约占总宽度的 8.33%
。咱们用 .is-列数
指定一个项目占据的列数:
.is-1
:占据 1 列,也就是 1/12 宽.is-2
:占据 2 列,也就是 1/6 宽.is-3
:占据 3 列,也就是 1/4 宽.is-4
:占据 4 列,也就是 1/3 宽.is-5
:占据 5 列,也就是 5/12 宽.is-6
:占据 6 列,也就是 1/2 宽.is-7
:占据 7 列,也就是 7/12 宽.is-8
:占据 8 列,也就是 2/3 宽.is-9
:占据 9 列,也就是 3/4 宽.is-10
:占据 10 列,也就是 5/6 宽.is-11
:占据 11 列,也就是 11/12 宽.is-12
:占据 12 列,也就是占满整个宽度根据这个规律,咱们能够很容易地写出栅格布局代码:
$columns: 12;
.row {
display: flex;
.col {
flex-grow: 1;
flex-basis: 0;
@for $i from 1 through 12 {
&.is-#{$i} {
flex: none;
width: percentage($i / 12);
}
}
}
}
复制代码
这里咱们使用 @for
指令的 @for $var from <start> through <end>
语法,从 1 递增到 12,定义了 .is-*
这一系列类名,原理就是咱们说过的禁用了 Flex 项目的伸缩特性,指定给它百分比宽度。怎么样,很简单吧。
接下来再加上折行(.row.is-multiline
)和 Flex 项目偏移(.is-offset-*
)的支持。
咱们总揽下代码:
$columns: 12;
.row {
display: flex;
&.is-multiline {
flex-wrap: wrap;
}
.col {
flex-grow: 1;
flex-basis: 0;
@for $i from 1 through 12 {
&.is-#{$i} {
flex: none;
width: percentage($i / 12);
}
&.is-offset-#{$i} {
margin-left: percentage($i / 12);
}
}
}
}
复制代码
.is-multiline
是跟随 .row
一块儿使用的,获得的就是 flex-wrap: wrap
的效果;项目偏移则借助 margin-left
属性实现。
到这里,咱们的 12 列栅格布局就写完了 ヾ(◍°∇°◍)ノ゙
咱们把上面两部分的代码整合起来,就能获得一个最小的响应式布局系统了~ O(∩_∩)O
$breakpoints: (
// Extra small screen / phone
xs: 0,
// Small screen / phone
sm: 576px,
// Medium screen / tablet
md: 768px,
// Large screen / desktop
lg: 992px,
// Extra large screen / wide desktop
xl: 1200px
);
$container-max-widths: (
xs: none,
sm: 540px,
md: 720px,
lg: 960px,
xl: 1140px
);
.container {
margin-left: auto;
margin-right: auto;
}
@each $device, $breakpoint in $breakpoints {
@media only screen and (min-width: $breakpoint) {
.container {
max-width: map-get($container-max-widths, $device);
}
}
}
$columns: 12;
.row {
display: flex;
&.is-multiline {
flex-wrap: wrap;
}
.col {
flex-grow: 1;
flex-basis: 0;
@for $i from 1 through 12 {
&.is-#{$i} {
flex: none;
width: percentage($i / 12);
}
&.is-offset-#{$i} {
margin-left: percentage($i / 12);
}
}
}
}
复制代码
能够在此查看效果。
固然,更多其余丰富的功能任君添加,这里只是提供了一个最简单的代码实现。
Grid system, by getbootstrap.com
感谢你花费宝贵的时间⏲️阅读这篇文章。
若是你以为这篇文章让你的生活美好了一点点,欢迎鼓(diǎn)励(zàn)😀。若是能在文章下面留下你宝贵的评论或意见是再合适不过的了,由于研究证实,参与讨论比单纯阅读更能让人对知识印象深入😉。
(完)