快速入手响应式布局

响应式布局

什么是响应式布局

响应式布局就是一个网站能够兼容多个终端,可以根据屏幕的大小自动调整页面的的展示方式以及布局,我们不用为每一个终端做一个特定的版本。

为什么要用响应式布局

  1. 面对不同分辨率设备灵活性强;
  2. 能够快捷解决多设备显示适应问题;
  3. 不用为了不同的设备写多套代码。

如何实现响应式布局

要实现响应式布局,必不可少的就是对css单位、视口(viewport)以及媒体查询的了解,所以在此之前,先看一些常用的css单位以及视口和媒体查询。

css单位

CSS 有几个不同的单位用于表示长度。常用的包括但不限于以下几个

  • px 绝对长度单位,1像素
  • em 相对长度单位,表示当前元素的字体尺寸,浏览器默认字体大小为16px,则1em == 16px
  • rem 相对长度单位,表示相对于根元素字体大小。用于根元素时,相对于初始字体大小
  • vh 相对长度单位,视窗高度,1vh=视窗高度的1%
  • vw 相对长度单位,视窗宽度,1vw=视窗宽度的1%
  • vmin 相对长度单位,vw和vh中较小的那个
  • vmax 相对长度单位,vw和vh中较大的那个
  • % 相对长度单位,相对于父元素的大小。用于根元素时,相对于视窗大小

CSS 单位

视口

viewport就是设备的屏幕上能用来显示我们的网页的那一块区域,在具体一点,就是浏览器上(也可能是一个app中的webview)用来显示网页的那部分区域。

移动设备上的浏览器都会把自己默认的viewport设为980px或1024px,但带来的后果就是浏览器会出现横向滚动条,因为浏览器可视区域的宽度是比这个默认的viewport的宽度要小的。

为了解决这一问题,我们通常都会使用下面这条代码。

<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

其中:
width=device-width表示宽度是设备屏幕的宽度;
initial-scale=1表示初始的缩放比例;
user-scalable=no表示用户是否可以调整缩放比例。
当缩放比例为是时,还可以设置以下两个属性:
minimum-scale=0.5表示最小的缩放比例;
maximum-scale=2.0表示最大的缩放比例。

viewport 深入理解
视口(viewport)简介 — Web移动手册阅读笔记

媒体查询

媒体查询也就是 @media查询,它可以针对不同的屏幕尺寸设置不同的样式,是响应式设计的关键组成部分。

也就是说我们可以通过设计多套css样式来达到响应式的效果。

他有以下几种使用方法:

  • 在css文件中
@media only screen and (max-width: 500px) {
    body {
        background-color: lightblue;
    }
}

上述代码表示如果视口小于 500px, 背景将变为浅蓝色。

其中,screen为媒体类型,表示应用于用于电脑屏幕、平板电脑、智能手机等显示设备上。常用的媒体类型还有

描述
all 所有设备
print 用于打印机和打印预览
screen 用于电脑屏幕、平板电脑、智能手机等
speech 用于屏幕阅读器等发声设备

only|and为限定词,用以对功能加以限制,还有一个not

max-width: 500px为媒体功能,表示所能接受的最大宽度为500px,只有在这个限制大小下的屏幕上css样式才会生效。常用的媒体功能还有

描述
max-width 定义输出设备中的页面最大可见区域宽度。
min-width 定义输出设备中的页面最小可见区域宽度。
max-height 定义输出设备中的页面最大可见区域高度。
min-height 定义输出设备中的页面最小可见区域高度。
width 定义输出设备中的页面可见区域宽度。
height 定义输出设备中的页面可见区域高度。
resolution 定义设备的分辨率。
device-width 定义输出设备的屏幕可见宽度。
device-height 定义输出设备的屏幕可见高度。
orientation 定义输出设备中的页面可见区域高度是否大于或等于宽度。
  • link
<link rel="stylesheet" media="only screen and (max-width: 500px)" href="mystylesheet.css">

上述代码表示如果视口小于 500px,则mystylesheet.css文件生效。其中media属性的用法和在css中一致。

  • 在js文件中
let mql = window.matchMedia('(max-width: 600px)');

document.querySelector(".mq-value").innerText = mql.matches;

上述代码表示判断当前视口的宽度是否小于或等于600px,并将结果显示在类名为mq-value的标签中。

Window 的matchMedia() 方法返回一个新的MediaQueryList 对象,表示指定的媒体查询 (en-US)字符串解析后的结果。返回的MediaQueryList 可被用于判定Document是否匹配媒体查询,或者监控一个document 来判定它匹配了或者停止匹配了此媒体查询。

Js也提供的方法用来监听视口的变化,以便实时响应。

var mql = window.matchMedia('(max-width: 600px)');

mql.addEventListener( "change", (e) => {
if (e.matches) {
/* the viewport is 600 pixels wide or less */
console.log('This is a narrow screen — less than 600px wide.')
document.body.style.backgroundColor = 'red';
} else {
/* the viewport is more than than 600 pixels wide */
console.log('This is a wide screen — more than 600px wide.')
document.body.style.backgroundColor = 'blue';
}
})

上述代码监听视口的宽度变化,当宽度大于600px时,页面背景色设为蓝色,当宽度小于等于600px时,页面背景设为红色。

当不需要的时候,也可以通过removeEventListener取消监听

mediaQueryList.removeEventListener("change", screenTest);

注意: 需要注意的是,IE浏览器不支持此监听方法。在IE10+中可以使用addListener/removeListener代替。

例子

/* iphone x 适配 */
@media (device-width: 375px) and (device-height: 812px) and (-webkit-min-device-pixel-ratio: 3) {
/* 设定条件为iphone x的设备宽高以及设备像素比 */
}

/* iphone xr 适配 */
@media (device-width: 414px) and (device-height: 896px) {
/* 设定条件为iphone xr的设备宽高 */
}

拓展:@规则

顾名思义,@规则就是用来制定一些特殊的规则,以@开头,后跟一个标识符,并包括直到下一个分号的所有内容, ‘;’, 或下一个CSS块,以先到者为准。就和属性一样,每种规则也会有不同的语法。

下面是一些常用的@规则

规则 含义
@charset 定义样式表使用的字符集
@import 告诉 CSS 引擎引入一个外部样式表
@namespace 告诉 CSS 引擎必须考虑XML命名空间
@media 如果满足媒介查询的条件则条件规则组里的规则生效
@keyframes 描述 CSS 动画的中间步骤
@supports 如果满足给定条件则条件规则组里的规则生效
@document 如果文档样式表满足给定条件则条件规则组里的规则生效

用以实现媒体查询的@media可以被归为条件规则组。条件规则组所指的条件 (类型不同) 总等效于 true 或者 false,如果为 true 那么它们里面的语句生效。同样被归为条件规则组的还有@supports@document

Window.matchMedia()
媒体查询入门指南
@规则

px + rem

在之前的章节,我们已经了解了rem的用法,因为rem是相对于根元素进行变化,所以我们可以利用这个特点,选择一个设备宽度作为基础,设置一个默认的根元素字体大小,然后根据比例计算rem大小。

px + rem是我们比较常用的一种方案。实现此方案需要几个步骤:

  1. 在根结点设置一个默认的字体大小;
  2. 对设计稿的标注进行转换,按第一步设置的字体大小,将px转换为rem;
  3. 对于需要等比缩放的元素,使用转换后的单位rem;
  4. 对于不需要等比缩放的元素,使用原单位px。

在使用此种方法的时候需要注意,我们需要动态的根据视口的宽度变化去设置不同的根字体大小。对此,我们可以借助淘宝团队开发的flexible.js来计算字体大小,以及单位转换。

示例

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 10rem;
height: 5rem;
margin: 0 auto;
background: #ccc;
}
span {
font-size: 24px;
}
</style>
</head>
<body>
<div class="box">
<span>这是一段文本</span>
</div>
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script>
</body>
</html>

在上面的示例中,字体的字号使用的仍然是px。这是因为我们在iPhone3G和iPhone4的Retina屏下面,希望看到的文本字号是相同的。也就是说,我们不希望文本在Retina屏幕下变小,另外,我们希望在大屏手机上看到更多文本,以及,现在绝大多数的字体文件都自带一些点阵尺寸,通常是16px和24px,所以我们不希望出现13px和15px这样的奇葩尺寸。

如此一来,就决定了在制作H5的页面中,rem并不适合用到段落文本上。

1px

腾讯网

使用Flexible实现手淘H5页面的终端适配

px + vw/vh

vw、vh分别将屏幕视口的宽和高等分成了100份。相对于rem的根据字体大小设计更加的简单,换算也更加的方便。

具体使用时,一般采用vw。因为在页面设计时,x轴方向上一般是不可滚动的,只有y轴是可以自由滚动的,视口高度的变化是不会引起页面的变化,所以我们需要基于宽度的变化去做自适应。

示例

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
margin: 0 auto;
width: 20vw;
height: 20vw;
background: #ccc;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>

px + %

百分比是相对于父元素的宽高的比例,bootstrap中的栅格系统就是利用的百分比和媒体查询来实现的响应式流式布局。

注意父元素没有明确的高度定义(指的是不定义height或者使用min/max-height这种,都属于不明确的高度定义),并且子元素使用百分比并且不是绝对定位,那么这时候的百分比等同于auto。

示例

这里直接采用bootstrap的栅格系统来做演示。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<style>
.box {
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="container">
<div class="box col-xs-12 col-sm-6 col-md-3">col-xs-12 col-sm-6 col-md-3</div>
<div class="box col-xs-12 col-sm-6 col-md-3">col-xs-12 col-sm-6 col-md-3</div>
<div class="box col-xs-12 col-sm-6 col-md-3">col-xs-12 col-sm-6 col-md-3</div>
</div>
</body>
</html>

flex布局

Flex是Flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。任何一个容器都可以指定为Flex布局。

这里只介绍一些常用的属性,详细的教程可以查看后文附的文章。

任何一个容器都可以指定为Flex布局。

.box{
display: -webkit-flex | -webkit-inline-flex;
display: flex | inline-flex;
}

注意设为 Flex 布局以后,子元素的floatclearvertical-align属性将失效。

然后我们可以设置主轴的方向。

.box {
flex-direction: row | row-reverse | column | column-reverse;
}

针对容器中的项目,可以设置其占据的大小,顺序,放大缩小比例等。

.item {
/* 排列顺序,越小越靠前,默认为0 */
order: 1;
/* 放大比例,默认为0 */
flex-grow: 0;
/* 缩小比例,默认为1 */
flex-shrink: 1;
/* 项目占据的主轴空间,与width属性一致,默认为auto */
flex-basis: 0;
/* 复合属性,flex-grow, flex-shrink 和 flex-basis的简写,后两个可选,默认为 0 1 auto */
/* 建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。 */
flex: 2;
}

示例

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
display: flex;
}

.flex {
flex: 1;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="box">
<div class="flex">1</div>
<div class="flex">2</div>
<div class="flex">3</div>
</div>
</body>
</html>

下面我们使用flex布局实现一个经典的圣杯布局。

圣杯布局

圣杯布局中,页面从上到下,分成三个部分:头部(header),躯干(body),尾部(footer)。其中躯干又水平分成三栏,从左到右为:导航、主栏、副栏。

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.container {
display: flex;
min-height: 100vh;
flex-direction: column;
}

header, footer {
border: 1px solid #ccc;
flex: 1;
}

.body {
display: flex;
flex: 1;
}

.content, .nav, .ads {
border: 1px solid #ccc;
}

.content {
flex: 1;
}

.nav, .ads {
/* 两个边栏的宽度设为12em */
flex: 0 0 12em;
}

.nav {
/* 导航放到最左边 */
order: -1;
}

@media (max-width: 768px) {
.body {
flex-direction: column;
flex: 1;
}

.nav, .ads, .content {
flex: auto;
}
}
</style>
</head>

<body>
<div class="container">
<header>header</header>
<div class="body">
<div class="content">content</div>
<div class="nav">nav</div>
<div class="ads">ads</div>
</div>
<footer>footer</footer>
</div>
</body>

</html>

Flex 布局教程:语法篇
Flex 布局教程:实例篇

Grid布局

网格布局(Grid)是最强大的 CSS 布局方案。它将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局。

Grid 布局与 Flex 布局有一定的相似性,都可以指定容器内部多个项目的位置。但是,它们也存在重大区别。
Flex 布局是轴线布局,只能指定”项目”针对轴线的位置,可以看作是一维布局。Grid 布局则是将容器划分成”行”和”列”,产生单元格,然后指定”项目所在”的单元格,可以看作是二维布局。Grid 布局远比 Flex 布局强大。

采用网格布局的区域,称为”容器”,容器里面的水平区域称为”行”(row),垂直区域称为”列”(column)。行和列的交叉区域,称为”单元格”(cell)。划分网格的线,称为”网格线”(grid line)。水平网格线划分出行,垂直网格线划分出列。

.box {
display: grid | inline-grid;
}

注意 设为网格布局以后,容器子元素(项目)的float、display: inline-block、display: table-cell、vertical-align和column-*等设置都将失效。

针对容器,我们还可以设置其每一列的列宽(grid-template-columns)和每一行的行高(grid-template-rows)。

.box {
display: grid;
grid-template-columns: 100px 200px 300px;
grid-template-rows: 100px 100px 100px;
}

上述代码指定了一个三行三列的网格,列宽依次为100px、200px、300px,行高均为100px。同样,也可以使用百分比或其他单位。

当行数或者列数多且重复时,可以借助css提供的函数repeat()来实现。

/* 简化重复值  */
.box {
display: grid;
/* repeath() 接受两个参数,第一个是重复次数,第二个是要重复的值 */
/* 三个列宽为33.33%的列 */
grid-template-columns: repeat(3, 33.33%);
/* 6行,一四行为100px,二五行为200px,三六行为300px */
grid-template-rows: repeat(2, 100px 200px, 300px);
}

.box {
/* 不知道列数,自动填充,直至放不下 */
grid-template-columns: repeat(auto-fill, 100px);
}

或者我们也可以使用fr关键字实现类似于flex的比例分配。

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

上述代码表示有两列,总宽度被分为3份,第一列占一份,第二列占两份。

当我们不确定宽度时,也可以使用范围函数(minmax())或者auto关键字。

.box {
display: grid;
grid-template-columns: 1fr 1fr minmax(100px, 1fr);
grid-template-rows: 100px auto 100px;
}

上面代码表示第三列的宽度在100px和1fr之间,第二行的高度由浏览器自己计算得出。

然后是间距。css为我们提供了三个属性,分别是:row-gapcolumn-gapgap

.box {
row-gap: 20px;
column-gap: 20px;
}
/* 上述等同于下述 */
.box {
/* 第一个值为行间距,第二个值为列间距。第二个值不写则默认两个值相等 */
gap: 20px 20px;
}

划分网格以后,容器的子元素会按照顺序,自动放置在每一个网格。默认的放置顺序是”先行后列”,即先填满第一行,再开始放入第二行。这个顺序由grid-auto-flow属性决定,默认值是row,即”先行后列”。也可以将它设成column,变成”先列后行”。

grid-auto-flow属性除了设置成row和column,还可以设成row dense和column dense。这两个值主要用于,某些项目指定位置以后,剩下的项目怎么自动放置。

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#container {
display: grid;
grid-template-columns: repeat(auto-fill, 33.33%);
grid-template-rows: repeat(auto-fill, 33.33%);
grid-auto-flow: row dense;
}

.item {
font-size: 4em;
text-align: center;
border: 1px solid #e5e4e9;
}

.item-1 {
/* 设置项目所占据的位置,值为网格线下标 */
grid-column-start: 1;
grid-column-end: 3;
}

.item-2 {
grid-column-start: 1;
grid-column-end: 3;
}
</style>
</head>

<body>
<div id="container">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item item-3">3</div>
<div class="item item-4">4</div>
<div class="item item-5">5</div>
<div class="item item-6">6</div>
<div class="item item-7">7</div>
<div class="item item-8">8</div>
<div class="item item-9">9</div>
</div>
</body>

</html>

CSS Grid 网格布局教程

css函数

calc()

calc() 函数用于动态计算长度值。

  • 需要注意的是,运算符前后都需要保留一个空格,例如:width: calc(100% - 10px);
  • 任何长度值都可以使用calc()函数进行计算;
  • calc()函数支持 “+”, “-“, “*”, “/“ 运算;
  • calc()函数使用标准的数学运算优先级规则;

calc()也是一个比较常用的函数,对于一些特殊的场景很有效。

示例

使用 calc() 可以很容易的为一个对象设置一个左右两边相等的外边距。

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
position: absolute;
left: 40px;
width: calc(100% - 80px);
border: solid black 1px;
box-shadow: 1px 2px;
background-color: yellow;
padding: 6px;
text-align: center;
box-sizing: border-box;

}
</style>
</head>

<body>
<div class="box"></div>
</body>

</html>

自动调整表单域的大小以适应其容器的大小

calc() 的另外一个用例是用来确保一个表单域的大小适合当前的可用空间,而不会在保持合适的外边距的同时,因挤压超出其容器的边缘。

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
input {
padding: 2px;
display: block;
width: calc(100% - 1em);
}

#formbox {
width: calc(100% / 6);
border: 1px solid black;
padding: 4px;
}
</style>
</head>

<body>
<div id="formbox">
<label>Type something:</label>
<input type="text">
</div>
</body>

</html>

constant() / env()

env()和constant(),是IOS11新增特性,Webkit的css函数,用于设定安全区域与边界的距离。env() 适用于IOS > 11.2版本的系统,constant()适用于IOS < 11.2 版本的系统。有4个预定义变量:

safe-area-inset-left:安全区域距离左边边界的距离
safe-area-inset-right:安全区域距离右边边界的距离
safe-area-inset-top:安全区域距离顶部边界的距离
safe-area-inset-bottom :安全距离底部边界的距离

为了告诉浏览器使用屏幕上所有的可用空间,并以此使用env()变量,我们需要添加一个新的视口元值(小程序中已经默认设置):

<meta name="viewport" content="... viewport-fit=cover">

这两个函数通常会与calc()一起使用。

.footer {
height: calc(100px+ constant(safe-area-inset-bottom));
height: calc(100px + env(safe-area-inset-bottom));
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}

上述是一种适配iOS底部安全距离的样式代码。

这里只介绍了几种和长度有关系的函数,其他的还有:attr()、hsl()、linear-gradient()、rgb()、var()等等。

文章作者: JaCo Wu
文章链接: https://jacokwu.cn/blog/2021/04/25/快速入手响应式布局/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 JaCo Wu的博客