Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

响应式布局,你应该知道的一切 #24

Open
campcc opened this issue Apr 15, 2021 · 0 comments
Open

响应式布局,你应该知道的一切 #24

campcc opened this issue Apr 15, 2021 · 0 comments

Comments

@campcc
Copy link
Owner

campcc commented Apr 15, 2021

1tjg32bo.jpg

2011年,Google 发布了 Android 4.0,在经历了 Cupcake,Donut,Froyo 等多个甜品名称版本的迭代后,安卓终结了 Symbian(塞班)的霸主地位,迅速占领了手机市场跃居全球第一。同年,腾讯发布了微信开始进军移动互联网,阿里也在 2013 年宣布 ALL IN 无线,随着智能设备的普及和移动互联网时代的到来,响应式布局这个词开始频繁地出现在 Web 设计和开发领域,作为一名优秀的前端攻城狮,要将极致的用户体验和最佳的工程实践作为探索的目标 ): balabala...

所以,响应式布局,要学。不仅要学,我们还要了解它的前世今生,前置知识,实现手段和原理,以便在实际应用时选取合适的技术方案。

阅读完本文,你将 Get 以下知识点,

  • 什么是响应式设计?
  • 什么是像素,什么DPR?设备像素与CSS像素的区别是什么?
  • EM,REM 的计算规则是什么?实际应用中如何选择?
  • 什么是视口 viewport,布局视口,视觉视口,理想视口的区别?
  • 百分比单位和视口单位的计算规则是什么?
  • 弹性盒与网格
  • 设备断点与 CSS 媒体查询
  • 响应式布局的一些最佳实践

响应式设计

著名的网页设计师 Ehan Marcotte 在 2010 年 5 月的一篇名为《Responsive Web Design》的个人文章中,首次提到了响应式网站设计。文中讲到响应式的概念源自响应式建筑设计,即房间或者空间会根据其内部人群数量和流动而变化。

最近一门新兴的学科“响应式建筑(responsive architecture)”开始在探讨物理空间根据流动于其中的人进行响应的方法。建筑师们通过把嵌入式机器人与可拉伸材料结合的方法,尝试艺术装置和可弯曲、伸缩和扩展的墙体结构,达到根据接近人群的情况变化的效果。运动传感器与气候控制系统相结合,调整围绕人们周围的房间的温度以及环境照明。已经有公司制造了“智能玻璃技术”,当室内人数达到一定的阀值时,它可以自动变为不透明状态,为人们提供更多隐私保护

Web 响应式设计的理念与之非常相似,只不过在这里,

我们需要适配的不是建筑,而是 Web 页面

我们期望页面可以根据用户的设备环境,比如系统,分辨率,屏幕尺寸等因素,进行自发式调整,提供更适合当前环境的阅读和操作体验,对已有和未来即将出现的新设备有一定的适应能力。

这就是响应式设计的理念。那么是否有对应的方法论呢?

别急,在谈及实现之前,我们需要了解一些前置知识,比如像素。

像素

什么是像素?

像素是图像中最小的单位,一个不可再分割的点,对应到物理设备上(比如计算机屏幕),就是屏幕上的一个光点。我们常说的分辨率就是长和宽上像素点的个数,比如 IPhone X 的分辨率是 1125×2436,代表屏幕横向和纵向分别有 1125 和 2436 个像素点,这里的像素是设备像素(Device Pixels)。

1px ≠1像素

实际开发中,你可能发现 Iphone X 的设计稿是 375×812,WTF?

这里的 375×812 是 CSS 像素,也叫虚拟像素,逻辑像素。为什么我们不使用设备像素呢?

设备像素对应屏幕上的光点,如今的屏幕分辨率已经达到人眼无法区分单个像素的程度了。试想一下,要在 IPhone X 宽不到 7cm 的屏幕上数出 1125 个像素点,想想就让人头疼。所以我们在实际开发中通常使用 CSS 像素,你眼中的 1px 可能对应多个设备像素,比如上面的 IPhone X,

1 css px = 3 * 3 device px // IPhone X 中,1 个 CSS 像素对应 3*3 的 9 个设备像素点

而上面这个比值 3 就是设备像素比(Device Pixel Ratio,简称 DPR)。

DPR 可以在浏览器中通过 JavaScript 代码获取,

window.devicePixelRatio // IPhone X 中等于 3,IPhone 6/7/8 中等于 2,Web 网页为 1

像素是一个固定单位,一般我们不会使用固定像素来做响应式布局,但是你需要了解他。相反,响应式布局里经常会用到相对单位,比如 EM。

EM

EM 相对于元素自身的 font-size

p {
  font-size: 16px;
  padding: 1em; /* 1em = 16px */
}

如果元素没有显式地设置 font-size,那么 1em 等于多少呢?

这个问题其实跟咱说的 em 没啥关系,这里跟 font-size 的计算规则相关,回顾一下。如果元素没有设置 font-size,会继承父元素的 font-size,如果父元素也没有,会沿着 DOM 树一直向上查找,直到根元素 html,根元素的默认字体大小为 16px。

理解了 EMREM 就很简单了。

REM

REM = Root EM,顾名思义就是相对于根元素的 EM。所以它的计算规则比较简单,

1 rem 就等于根元素 html 的字体大小,

html {
  font-size: 14px;
}

p {
  font-size: 1rem; /* 1rem = 14px */
}

所以,如果我们改变根元素的字体大小,页面上所有使用 rem 的元素都会被重绘。

EM 和 REM 都是相对单位,我们在做响应式布局的时候应该如何选择呢?

根据两者的特性,

  • EM 更适合模块化的页面元素,比如 Web Components
  • REM 则更加方便,只需要设置 html 的字体大小,所以 REM 的使用更加广泛一些

实际开发中,设计图的单位是 CSS 像素,我们可以借助一些工具将 px 自动转换为 rem,

下面是一个用 PostCSS 插件在基于 Webpack 构建的项目中自动转换的例子,

var px2rem = require('postcss-px2rem');

module.exports = {
  module: {
    loaders: [
      {
        test: /\.css$/,
        loader: "style-loader!css-loader!postcss-loader"
      }
    ]
  },
  postcss: function() {
    return [px2rem({remUnit: 75})];
  }
}

我们已经有响应式单位了,接下来要怎么让页面支持响应式布局呢?

第一步需要先设置页面的 viewport

Viewport

著名的 JavaScript 专家 Peter-Paul Koch 曾发表过三篇有关 viewport 的文章,

建议先看完上述文章。viewport 最先由 Apple 引入,用于解决移动端页面的显示问题,通过一个叫 <meta> 的 DOM 标签,允许我们可以定义视口的各种行为,比如宽度,高度,初始缩放比例等,

<!-- 下面的 meta 定义了 viewport 的宽度为屏幕宽度,单位是 CSS 像素,默认不缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1">

Peter-Paul Koch 在文章中将移动浏览器的视口分为三种。

layout viewport

为了解决早期 Web 页面在手持设备上的显示问题,Apple 在 IOS Safari 中定义了一个 viewport meta 标签,它可以创建一个虚拟的布局视口(layout viewport),这个视口的分辨率接近于 PC 显示器。这样一来,由于两者的宽度趋近,CSS只需要像在PC上那样渲染页面就行,原有的页面结构也基本不会被破坏。

layout viewport 是一个固定的值,由浏览器厂商设定,

  • IOS 和 Android 基本都是 980px
  • 黑莓(BlackBerry)和 IE10 是 1024px

可以通过 document 获取布局视口的宽度和高度,

var layoutViewportWidth = document.documentElement.clientWidth
var layoutViewportHeight = document.documentElement.clientHeight

visual viewport

视觉视口(visual viewport)可以简单理解为手持设备物理屏幕的可视区域。也就是你的手机屏幕,所以不同设备的视觉视口可能不同,有了 visual viewport,我们就可以实现网页的拖拽和缩放了,为什么?

因为有了一个承载布局视口的容器

试想一下,假如我们现在有一台 IPhone 6(375×627),它会在宽为 375px 的 visual viewport 上,创建一个宽为 980px 的 layout viewport,于是用户可以在 visual viewport 中拖动或缩放网页来获得更好的浏览体验。

视觉视口可以通过 window 获取,

var visualViewportWidth = window.innerWidth
var visualViewportHeight = window.innerHeight

idea viewport

我们前面一直在讨论 Web 页面在移动浏览器上的适配问题,但是如果网页本来就是为移动端设计的,这个时候布局视口(layout viewport)反而不太适用了,所以我们还需要另一种布局视口,它的宽度和视觉视口相同,用户不需要缩放和拖动网页就能获得良好的浏览体验,这就是理想视口(idea viewport)。

我们可以通过 meta 设置将布局视口转换为理想视口,

<meta name="viewport" content="width=device-width">

meta

视口可以通过 <meta> 进行设置,viewport 元标签的取值有 6 种,

  • width,正整数 | device-width,视口宽度,单位是 CSS 像素,如果等于 device-width,则为理想视口的宽度
  • height,正整数 | device-width,视口宽度,单位是 CSS 像素,如果等于 device-height,则为理想视口的高度
  • initial-scale,0-10,初始缩放比例,允许小数点
  • minimum-scale,0-10,最小缩放比例,必须小于等于 maximum-scale
  • maximum-scale,0-10,最大缩放比例,必须大于等于 minimum-scale
  • user-scalable,yes/no,是否允许用户缩放页面,默认是 yes

了解了视口之后,让我们回到响应式布局,与视口相关的几个单位有:vw,vh,百分比

vw,vh,百分比

浏览器对于 vwvh 的支持相对较晚,在 Android 4.4 以下的浏览器中可能没办法使用,下面是来自 Can I use 完整的兼容性统计数据,

image.png

新生特性往往逃不过兼容性的大坑,但是这并不妨碍我们了解它。

响应式设计里,vwvh 常被用于布局,因为它们是相对于视口的,

  • vw,viewport width,视口宽度,所以 1vw = 1% 视口宽度
  • vh,viewport height,视口高度,所以 1vh = 1% 视口高度

以 IPhone X 为例,vw 和 CSS 像素的换算如下,

<!-- 假设我们设置视口为完美视口,这时视口宽度就等于设备宽度,CSS 像素为 375px -->
<meta name="viewport" content="width=device-width, initial-scale=1">

<style>
  p {
    width: 10vw; /* 10vw = 1% * 10 * 375px = 37.5px */
  }
</style>

我们说百分比也可以用来设置元素的宽高,它和 vwvh 的区别是什么?

这里只需要记住一点,百分比是相对于父元素的宽度和高度计算的。

到这里,相信你已经掌握了响应式布局里常用的所有单位。接下来,我们介绍弹性盒和栅格,它们都不是单位,而是一种新的布局方案。

弹性盒

W3C 在 2009 年提出了弹性盒,截止目前浏览器对 FlexBox 的支持已经相对完善,下面是 Can I use FlexBox 完整的兼容性情况,

image.png

关于弹性盒模型推荐阅读这篇文章 A Complete Guide to Flexbox

假设你已经阅读完并了解了弹性盒模型,响应式布局中我们需要关注 FlexBox 里的两个角色:容器和子元素

container

指定 display 属性为 flex,就可以将一个元素设置为 FlexBox 容器,我们可以通过定义它的属性,决定子元素的排列方式,属性可选值有 6 种,

  • flex-direction,主轴方向,也就是子元素排列的方向
  • flex-wrap,子元素能否换行展示及换行方式
  • flex-flow,flex-direction 和 flex-wrap 的简写形式
  • justify-content,子元素在主轴上的对齐方式
  • align-items,子元素在垂直于主轴的交叉轴上的排列方式
  • align-content,子元素在多条轴线上的对齐方式

items

子元素也支持 6 个属性可选值,

  • order,子元素在主轴上的排列顺序
  • flex-grow,子元素的放大比例,默认 0
  • flex-shrink,子元素的缩小比例,默认 1
  • flex-basis,分配剩余空间时,子元素的默认大小,默认 auto
  • flex,flex-grow,flex-shrink,flex-basis 的简写
  • align-self,覆盖容器的 align-items 属性

弹性盒模型布局非常灵活,属性值也足够应对大部分复杂的场景,但 FlexBox 基于轴线,只能解决一维场景下的布局,作为补充,W3C 在后续提出了网格布局(CSS Grid Layout),网格将容器再度划分为 “行” 和 “列”,产生单元格,项目(子元素)可以在单元格内组合定位,所以网格可以看作二维布局。

网格

关于网格布局推荐阅读这篇文章 A Complete Guide to Grid

上述文章非常详细地介绍了网格的一些基本概念(比如容器和项目,行和列,单元格和网格线等),使用姿势,注意事项等。作为新兴的布局方案,使用时你需要考虑兼容性是否满足,

image.png

不过在标准之外,我们可能也正通过其他的一些姿势在使用网格。如果你关注时下一些比较热门的 UI 库,比如 Ant DesginMaterial UIElement Plus 等,它们以栅格系统的方式实现了对网格部分特性的支持。

UI 库对 Grid 的实现中,通常会使用到媒体查询,这也是响应式布局的核心技术。

媒体查询

媒体查询(Media Query)是 CSS3 规范中的一部分,媒体查询提供了简单的判断方法,允许开发者根据不同的设备特征应用不同的样式。响应式布局中,常用的设备特征有,

  • min-width,数值,视口宽度大于 min-width 时应用样式
  • max-width,数值,视口宽度小于 max-width 时应用样式
  • orientation,portrait | landscape,当前设备的方向

选择 min-widthmax-width 取值的过程,称为设备断点选择,它可能取决于产品设计本身,下面是 百度 Web 生态团队 总结的一套比较具有代表性的设备断点,

/* 很小的设备(手机等,小于 600px) */
@media only screen and (max-width: 600px) { }

/* 比较小的设备(竖屏的平板,屏幕较大的手机等, 大于 600px) */
@media only screen and (min-width: 600px) { }

/* 中型大小设备(横屏的平板, 大于 768px) */
@media only screen and (min-width: 768px) { }

/* 大型设备(电脑, 大于 992px) */
@media only screen and (min-width: 992px) { }

/* 超大型设备(大尺寸电脑屏幕, 大于 1200px) */
@media only screen and (min-width: 1200px) { }

如果你需要对细分屏幕大小进行适配,ResponsiveDesign 站点上的这篇文章 Media queries for common device breakpoints 可能会有所帮助。

响应式文字和图片

相信你已经掌握了响应式布局的所有知识,接下来我们介绍一些最佳实践。

文字

大多数用户阅读都是从左到右,如果一行文字太长,阅读下一行时容易出错,或者用户只会读一行文字的前半部分,而略读后半部分。在上世纪就有研究表明,一行 45 ~ 90 个英文字符是最好的,对于汉字来说,一行文字合理的数量应该是 22 ~ 45 个字符。

此外,字体大小对阅读体验同样重要,基本字体一般不小于 16px,行高大于 1.2em

p {
  font-size: 16px;
  line-height: 1.2em; /* 1.2em = 19.2px */
}

图片

《高性能网站建设指南》的作者 Steve Souders 曾在 2013 年的一篇 博客 中提到:

我的大部分性能优化工作都集中在 JavaScript 和 CSS 上,从早期的 Move Scripts to the Bottom 和 Put Stylesheets at the Top 规则。为了强调这些规则的重要性,我甚至说过,“JS 和 CSS 是页面上最重要的部分”。几个月后,我意识到这是错误的。图片才是页面上最重要的部分。

图片几乎占了网页流量消耗的 60%,雅虎军规和 Google 都将图片优化作为网页优化不可或缺的环节,除了图片性能优化外,响应式图片无疑带来更好的用户体验。

下面是一些响应式图片的最佳实践,

1.确保图片内容不会超出 viewport

试想一下,如果图片固定大小且超出理想视口的宽度,会发生什么?

内容会溢出视口外,导致出现横向滚动条对不对,这在移动端是非常不好的浏览体验,因为用户往往更习惯上下滚动,而不是左右滚动,所以我们需要确保图片内容不要超出 viewport,可以通过设置元素的最大宽度进行限制,

img {
  max-width: 100%;
}

类似的,相同的规则也应该用于一些其他的嵌入式元素,比如 embed,object,video 等。

2. 图片质量支持响应式

这是一种支持优雅降级的方案,现代浏览器已经支持了 srcsetsizes 属性,对于兼容性不好的浏览器,会继续使用默认 src 属性中的图片,所以我们可以放心大胆的使用。

  • srcset 支持定义几组图片和对应的尺寸
  • sizes 支持一组媒体查询条件
<!-- 响应式图片 -->
<img
  srcset="example-320w.jpg 320w,
          example-480w.jpg 480w,
          example-800w.jpg 800w"
  sizes="(max-width: 320px) 280px,
         (max-width: 480px) 440px,
         800px"
  src="example-800w.jpg" alt="An example image">

如果我们书写了上面代码中的图片,浏览器会根据下面的顺序加载图片,

  1. 获取设备视口宽度
  2. 从上到下找到第一个为真的媒体查询
  3. 获取该条件对应的图片尺寸
  4. 加载 srcset 中最接近这个尺寸的图片并显示

除了上述方式外,我们也可以使用 HTML5 标准中的 picture 标签实现类似的效果,

<picture>
  <source media="(max-width: 799px)" srcset="example-480w-portrait.jpg">
  <source media="(min-width: 800px)" srcset="example-800w.jpg">
  <img src="example-800w.jpg" alt="An example img">
</picture>

小结

我们从响应式布局的设计角度出发,介绍了响应式的设计理念,前置知识(像素,DPR,视口等),相对单位(em,rem,百分比,vw,vh等),布局方案(FlexBox,Gird)以及媒体查询等技术,其中不乏很多前辈们的最佳实践,作为开发者我们应该用这些经验,以更好地优化不同尺寸大小设备的用户体验。

参考链接

写在最后

本文首发于我的 博客,才疏学浅,难免有错误,文章有误之处还望不吝指正!

如果有疑问或者发现错误,可以在相应的 issues 进行提问或勘误

如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励

(完)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant