【学习笔记】CSS 布局

一些常见基本概念

  • 物理像素(physical pixel):又被称为设备像素,它是显示设备中一个最微小的物理部件,每一个像素可根据操做系统设置本身的颜色和亮度,正是这些设备像素的微小距离欺骗了肉眼看到的图像效果
  • 设备独立像素(density-independent pixel):设备独立像素也称为密度无关像素,能够认为是计算机坐标系统中的一个点,这个点表明一个能够由程序使用的虚拟像素(如 CSS 像素),而后由相关系统转换为物理像素
  • CSS 像素:CSS 像素是一个抽像单位,主要使用在浏览器上,用来精确度量 Web 页面上的内容,通常状况下 CSS 像素被称为与设备无关的像素(device-independent pixel),简称 DIPs
  • 屏幕密度(PPI):屏幕密度是指一个设备表面上存在的像素数量,它一般以每英寸有多少像素来计算
  • 设备像素比(device pixel ratio):简称为 dpr,其定义了物理像素和设备独立像素的对应关系
    • 设备像素比 = 物理像素 / 设备独立像素
    • 在 JS 中能够经过 window.devicePixelRatio 获取到当前设备的 dpr
    • 在 CSS 中可经过 -webkit-device-pixel-ratio、-webkit-min-device-pixel-ratio、-webkit-max-device-pixel-ratio 进行媒体查询,对不一样 dpr 的设备作一些样式适配,或使用 resolution | min-resolution | max-resolution 这些比较新的标准方式

image.png

  • 位图像素
    • 一个位图像素是栅格图像(如 png, jpg, gif 等)最小数据单元,每一个位图像素都包含着一些自身的显示信息(如显示位置、颜色值、透明度等)
    • 理论上一个位图像素对应于一个物理像素,图片才能获得完美清晰的展现,以下图:对于 dpr=2 的 retina 屏幕而言,一个位图像素对应于 4 个物理像素,因为单个位图像素不能够再进一步分割,因此只能就近取色,从而致使图片模糊
    • 对于图片高清问题,比较好的方案就是两倍图片(@2x),如 200×300(CSS pixel) img 标签,需提供 400×600 的图片

image.png

  • 缩放比 scale:scale = 1/dpr
  • 视窗 viewport:简单理解 viewport 是严格等于浏览器窗口,在桌面浏览器中 viewport 就是浏览器窗口的宽高,但在移动端设备上有点复杂。阅读推荐:解读 viewport—网页自适应移动 app 神器
  • 媒体查询 @media
    • 媒体查询由一个可选的媒体类型和零个或多个使用媒体功能的限制了样式表范围的表达式组成,例如宽度、高度和颜 色。媒体查询添加自 CSS3,容许内容的呈现针对一个特定范围的输出设备而进行裁剪,而没必要改变内容自己,很是适 合web网⻚应对不一样型号的设备而作出对应的响应适配
    • 媒体查询包含一个可选的媒体类型和,知足 CSS3 规范的条件下,包含零个或多个表达式,这些表达式描述了媒体特征,最终会被解析为 true 或 false。若媒体查询中指定的媒体类型匹配展现文档所使用的设备类型且全部的表达式的值都是true,则该媒体查询的结果为 true,那么媒体查询内的样式将会生效
    <!-- link 元素中的 CSS 媒体查询 -->
    <link rel="stylesheet" media="(max-width: 800px)" href="example.css" />
    <!-- 样式表中的CSS媒体查询 --> 
    <style> @media (max-width: 767px) { ...css代码... } @media (min-width: 768px) and (max-width: 991px) { ...css代码... } </style>
    复制代码

    阅读推荐:随方逐圆 -- 全面理解 CSS 媒体查询css

一些布局概念

静态布局(static layout)html

  • 传统 Web 设计,网页上的全部元素的尺寸一概使用 px 做为单位,无论浏览器尺寸具体多少,网页布局始终按照最初代码的布局来显示
  • 设计方法
    • pc 端:居中布局,全部样式使用绝对宽度/高度 (px) 设计一个 Layout,在屏幕宽高有调整时使用横向和竖向的滚动条来查阅被遮掩部分
    • 移动设备:另外创建移动网站,单独设计一个布局,使用不一样的域名如 wap. 或 m. 等
  • 优势:这种布局方式对设计师和 CSS 编写者来讲都是最简单的,亦没有兼容性问题
  • 缺点:这种布局方式不能根据用户的屏幕尺寸作出不一样的表现,固定像素尺寸的网页是匹配固定像素尺寸显示器的最简单办法,但这种方法不是一种彻底兼容目前网页的制做方法,须要一些适应未知设备的方法

流式布局(Liquid Layout)前端

  • 流式布局(Liquid)的特色(也叫 "Fluid") 是页面元素的宽度按照屏幕分辨率进行适配调整,但总体布局不变,表明做如:栅栏系统(网格系统)
  • 使用 % 百分比定义宽度,高度大都是用 px 来固定。可根据可视区域 (viewport) 和父元素的实时尺寸进行调整,尽量的适应各类分辨率,每每配合 max-width/min-width 等属性控制尺寸流动范围以避免过大或者太小影响阅读(图片等也是如此,width:100%, max-width 通常设定为图片自己的尺寸,防止被拉伸而失真)
  • 布局特色:屏幕分辨率变化时页面里元素大小会变化但布局不变,这就致使若屏幕太大或过小都会致使元素没法正常显示
  • 优势:该布局方式在 Web 前端开发的早期历史上用来应对不一样尺寸的 PC 屏幕(那时屏幕尺寸差别不会太大),在移动端开发也是经常使用布局方式
  • 缺点:主要问题是若屏幕尺度跨度太大,那在相对其原始设计而言太小或过大的屏幕上不能正常显示,由于宽度使用 % 百分比定义,但高度和文字大小等大都是用 px 来固定,因此在大屏幕手机下显示效果会变成有些页面元素宽度被拉的很长,但高度、文字大小仍是和原来同样(即这些东西没法变得“流式”),显示很是不协调

自适应布局(Adaptive Layout)android

  • 特色:建立多个静态布局,每一个静态布局对应一个屏幕分辨率范围,使用 @media 媒体查询来切换多个布局,改变屏幕分辨率能够切换不一样的静态局部(页面元素位置发生改变),但在每一个静态布局中页面元素不随窗口大小的调整发生变化,能够把自适应布局看做是静态布局的一个系列
  • 屏幕分辨率变化时页面里面元素的位置会变化而大小不会变化
  • 设计方法:使用 @media 媒体查询给不一样尺寸和介质的设备切换不一样的样式,在优秀的响应范围设计下能够给适配范围内的设备最好的体验

响应式布局(Responsive Layout)css3

  • 响应式设计的目标是确保一个页面在全部终端上(各类尺寸的PC、手机、手表、冰箱的 Web 浏览器等)都能显示出使人满意的效果,对 CSS 编写者而言,在实现上不拘泥于具体手法,但一般是糅合了流式布局 + 弹性布局,搭配媒体查询技术使用,分别为不一样的屏幕分辨率定义布局(响应式几乎已经成为优秀页面布局的标准)
  • 布局特色:每一个屏幕分辨率下面会有一个布局样式,即元素位置和大小都会变
  • 优势:适应 pc 和移动端,若是足够耐心,效果完美
  • 缺点:
    • 媒体查询是有限的,便可枚举出来的只能适应主流的宽高
    • 要匹配足够多的屏幕大小,工做量不小,设计也须要多个版本

弹性布局(rem/em 布局)git

  • rem 是相对于 HTML 元素的 font-size 大小而言的,而 em 是相对于其父元素的(非 font-size 属性是相对于自身的 font-size )
  • 使用 em 或 rem 相对 % 百分比更加灵活,同时可支持浏览器的字体大小调整和缩放等的正常显示
  • 布局特色:包裹文字的各元素的尺寸采用 em/rem 作单位,而页面的主要划分区域的尺寸仍使用百分数或 px 作单位(同「流式布局」或「静态/固定布局」),早期浏览器不支持整个页面按比例缩放,仅支持网页内文字尺寸的放大,这种状况下使用 em/rem 作单位,可使包裹文字的元素随着文字的缩放而缩放
  • 浏览器的默认字体高度通常为 16px,即 1em: 16px,可是 1:16 的比例不方便计算,为了使单位 em/rem 更直观,CSS 编写者经常将页面跟节点字体设为 62.5%,如选择用 rem 控制字体时需先设置根节点 html 的字体大小,因浏览器默认字体大小16px * 62.5% = 10px,这样 1rem 即是 10px,方便了计算
  • 用 em/rem 定义尺寸的另个好处是更能适应缩进、以字体单位 padding 或 margin、浏览器设置字体尺寸等状况,由于 em/rem 相对于字体大小会同步改变,如:p{ text-indent: 2em; }
  • 使用 rem 单位的弹性布局在移动端也很受欢迎,淘宝的 Flexible 让 rem 布局得以流行开来,而此 Flexible 实现也有一些不足,此外也涌现出了多种实现 rem 布局的方案,如直接使用 html{ font-size:625%; } 基准值配合 JS 来设置根元素字体大小或者使用媒体查询来设置根元素字体大小等

传统常见布局方式

文档流布局

这是最基本的布局方式,按照文档的顺序一个一个显示出来,块元素独占一行,行内元素共享一行github

浮动布局

浮动方式布局就是使用 float 属性,使元素脱离文档流浮动起来web

定位布局

经过 position 属性来进行定位浏览器

table 布局

  • 直接用 table 等标签布局,table 布局自动垂直居中
  • table 的好处:在某些场合使用 table 是彻底合适、恰当和准确的,如用 table 作表格,若没法判断是否应该使用 table,请问本身几个问题,若是答案仅仅是“我猜……也许不是”,那么就不该该用table
    • “是否这些行或者列的信息共享某一个属性?好比每行显示一个学生的信息,全部学生都有个‘姓名’属性”
    • “若是我改变了这些行或者列的顺序,是否是依然有意义或者有一样的效果?”
    • “若是将行变成列或者将列变成行,是否是依然有意义或者有一样的效果?”
  • 在实际的项目开发过程当中,不建议用 table 进行布局,缘由以下:
    • table 比其它 html 标记占更多的字节(形成下载时间延迟,占用服务器更多流量资源)
    • table 会阻挡浏览器渲染引擎的渲染顺序(会延迟页面的生成速度,让用户等待更久的时间),table 必须在页面彻底加载后才显示,没有加载完毕前 table 为一片空白,即须要页面完毕才显示,而 div 是逐行显示,不须要页面彻底加载完毕就能够一边加载一边显示
    • table 里显示图片时须要把单个、有逻辑性的图片切成多个图(增长设计的复杂度,增长页面加载时间,增长 http 会话数)
    • 在某些浏览器中,table 里的文字的拷贝会出现问题(会让用户不悦)
    • table 会影响其内部的某些布局属性的生效(如元素的 height:100%) (限制页面设计的自由性)
    • 一旦学了 CSS 的知识,会发现使用 table 作页面布局会变得更麻烦(先花时间学一些 CSS 知识,会省去你之后大量的时间)
    • table 代码会让阅读者抓狂(不但没法利用 CSS,并且会不知所云,尤为在进行页面改版或内容抽取时)
    • table一旦设计完成就变成死的,很难经过CSS让它展示新的面貌(CSS ZEN GARDEN:www.csszengarden.com/)

div + css 的布局较 table 布局有什么优势?
改版时更方便,只要改css文件
页面加载速度更快、结构化清晰、页面显示简洁,表现与结构相分离
易于优化(seo)搜索引擎更友好sass

flex 布局

flex 是什么

2009 年对前端来讲是不平凡的一年,HTML5 定稿、ES5.1 发布、flex 应运而生。flex 是 flexible box 的缩写,意为"弹性布局",天生响应式,生而为布局,使用及其简单,能够简便、完整、响应式地实现各类页面布局
根据规范中的描述可知道,flexbox 模块提供了一个有效的布局方式,即便不知道视窗大小或未知元素状况之下均可以智能的、灵活的调整和分配元素和空间二者之间的关系。简单的理解就是能够自动调整,计算元素在容器空间中的大小

  • flex 是一种新型的布局方式,使用该布局方式能够实现几乎全部想要的效果。可是要注意其浏览器的兼容性,flex 只支持 ie 10+,全部仍是要根据项目状况使用,具体可查阅:Can I use 查阅兼容性状况

image.png

FFC (flex formatting context)

  • flexbox 布局新定义了格式化上下文,相似 BFC(block formatting context),除了布局和一些细节不一样之外的一切规则都和 BFC 是相同的

    注意 : 这里所指的 flexbox 是指设置了 display: flex; 或 display: inline-flex; 的盒子,不是指单单设置了 display: flex; 的盒子

  • 例如,设置了 display: flex; 或 display: inline-flex 的元素,和 BFC 同样,不会被浮动的元素遮盖,不会垂直外边距坍塌等等
  • 对于设置了 display: inline-flex 的盒子来讲,能够类比 display: inline-box; 理解,它不会独占一行,可是能够设置宽和高
  • 与 BFC 的细微区别
    • flexbox 不支持 ::first-line 和 ::first-letter 这两种伪元素
    • vertical-align 对 flexbox 中的子元素是没有效果的
    • float 和 clear 属性对 flexbox 中的子元素是没有效果的,也不会使子元素脱离文档流(可是对 flexbox 是有效果的!)
    • 多栏布局(column-*) 在 flexbox 中也是失效的,即不能使用多栏布局在flexbox 排列其下的子元素(鱼和熊掌不可得兼嘛)
    • flexbox 下的子元素不会继承父级容器的宽

对于 flex 盒模型的设计指望

  • 在任何流动的方向上(包括上下左右)都能进行良好的布局
  • 能够以逆序或以任意顺序排列布局
  • 能够线性的沿着主轴一字排开或沿着侧轴换行排列
  • 能够弹性的在任意的容器中伸缩大小
  • 可使子元素们在容器主轴方向上或在容器侧轴方向上进行对齐
  • 能够动态的沿着主轴方向伸缩子级的尺寸,与此同时保证父级侧轴方向上的尺寸

基本概念

  • 在 flex 容器中默认存在两条轴,水平主轴(main axis) 和垂直的交叉侧轴(cross axis),这是默认的设置,固然可经过修改使垂直方向变为主轴,水平方向变为交叉轴

  • 容器中的每一个单元块被称为 flex item,每一个项目占据的主轴空间为 (main size), 占据的交叉轴的空间为 (cross size)

    注意不能先入为主认为宽度就是 main size,高度就是 cross size,这个还要取决于主轴方向,若垂直方向是主轴,那项目的高度就是 main size

  • writing-mode 属性可改变文档流方向,此时主轴是垂直方向,但实际开发不多遇到这样场景,所以初学时直接使用水平方向和垂直方向理解不会有任何问题,反而易于理解

image.png

使用 flex 布局

  • 实现 flex 布局需先指定一个容器,任何一个容器只要被指定为 flex 布局,该容器内部元素就可以使用 flex 进行布局

    • 给 div 这类块状元素元素设置 display: flex 或给 span 这类内联元素设置 display: inline-flex,Flex布局即建立!其中,直接设置 display: flex 或 display: inline-flex 的元素称为 flex 容器,里面的子元素称为 flex 子项
    • 注意:当时设置 flex 布局后子元素的 float、clear、vertical-align 的属性将会失效
  • flex 布局相关属性分为两拨:一拨做用在 flex 容器上(此处以“父容器”代替),还有一拨做用在 flex 子项(此处以“子容器”代替)上,具体参见下表,不管做用在父容器上,仍是做用在子容器,都是控制子容器的呈现,只是前者控制的是总体,后者控制的是个体

    做用在父容器 做用子容器
    flex-direction order
    flex-wrap flex-grow
    flex-flow flex-shrink
    justify-content flex-basis
    align-items flex
    align-content align-self
  • 做用在父容器上的属性
    flex-direction

    • 决定主轴的方向(主轴默认是水平方向,从左至右),用来控制子项总体布局方向,从左往右仍是从右往左,从上往下仍是从下往上。和 CSS 的 direction 属性相比就是多了个 flex
    // row:默认值,显示为行,方向为当前文档水平流方向(主轴),默认状况下是从左往右。若是当前水平文档流方向是 rtl(如设置 direction: rtl),则从右往左
    // row-reverse:显示为行,但方向和 row 属性值是反的
    // column:显示为列,垂直方向,起点在上沿
    // column-reverse:显示为列,垂直方向,但方向和 column 属性值是反的
    flex-direction: row | row-reverse | column | column-reverse;
    复制代码

    image.png

    flex-wrap

    • 决定子容器是否换行显示,默认状况下项目都排在主轴线上,使用 flex-wrap 可实现项目的换行
    // nowrap:默认值,表示单行显示不换行,即当主轴尺寸固定、空间不足时,项目尺寸会随之调整而并不会挤到下一行
    // wrap:子容器主轴总尺寸超出父容器时换行,第一行在上方
    // wrap-reverse:宽度不足换行显示,可是从下往上开始,即本来换行在下面的子项跑到上面
    flex-wrap: nowrap | wrap | wrap-reverse;
    复制代码

    image.png

    flex-flow

    • 是 flex-direction 和 flex-wrap 的缩写形式,表示 flex 布局的 flow 流动特性,默认值为 row nowrap
    flex-flow: <flex-direction> || <flex-wrap>;
    复制代码

    justify-content

    • 决定了水平方向子容器的对齐和分布方式,CSS text-align 有个属性值为 justify,可实现两端对齐
    • justify-content 能够当作是 text-align 的远房亲戚,不过前者控制 flex 元素的水平对齐外加分布,后者控制内联元素的水平对齐
    // flex-start:默认值,逻辑 CSS 属性值,与文档流方向相关,默认表现为左对齐
    // flex-end:逻辑CSS属性值,与文档流方向相关,默认表现为右对齐
    // center:表现为居中对齐
    // space-between:表现为两端对齐,between 是中间的意思,意思是多余的空白间距只在元素中间区域分配
    // space-around:around 是环绕的意思,意思是每一个子容器两侧都环绕互不干扰的等宽的空白间距,最终视觉上边缘两侧的空白只有中间空白宽度一半
    // space-evenly:evenly 是匀称、平等的意思,即视觉上每一个子容器两侧空白间距彻底相等
    
    justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly;
    复制代码

    image.png

    align-items

    • align-items 中的 items 指的就是子容器,所以 align-items 指的就是 子容器相对于父容器在垂直方向上的对齐方式
    // stretch:默认值,子容器拉伸,即若子容器未设置高度或设为 auto,将占满整个父容器的高度,若子容器设置了高度,则按照设置的高度值渲染,而非拉伸
    // flex-start:逻辑 CSS 属性值,与文档流方向相关,默认表现为容器顶部对齐
    // flex-end:逻辑 CSS 属性值,与文档流方向相关,默认表现为容器底部对齐
    // center:表现为垂直居中对齐
    // baseline:表现为全部 =子容器都相对于父容器的基线(字母 x 的下边缘)对齐
    align-items: stretch | flex-start | flex-end | center | baseline;
    复制代码

    image.png

    align-content

    • align-content 可当作和 justify-content 是类似且对立的属性,justify-content 指明水平方向子容器的对齐和分布方式,而 align-content 则是指明垂直方向每一行子容器的对齐和分布方式,若全部子容器只有一行,则 align-content 属性是没有任何效果的
    • 当 flex-wrap 设置为 nowrap 时,容器仅存在一根轴线,由于项目不会换行则不会产生多条轴线,当 flex-wrap 设置为 wrap 时,容器可能会出现多条轴线,这时就须要去设置多条轴线之间的对齐方式了
    // stretch:默认值,每行子容器都等比例拉伸,如若共两行子容器,则每一行拉伸高度是 50%
    // flex-start:逻辑CSS属性值,与文档流方向相关,默认表现为顶部堆砌
    // flex-end:逻辑CSS属性值,与文档流方向相关,默认表现为底部堆放
    // center:表现为总体垂直居中对齐
    // space-between:表现为上下两行两端对齐。剩下每一行元素等分剩余空间
    // space-around:每一行元素上下都享有独立不重叠的空白空间
    // space-evenly:每一行元素都彻底上下等分
    align-content: stretch | flex-start | flex-end | center | space-between | space-around | space-evenly;
    复制代码

    image.png

  • 做用在子容器上的属性
    order

    • 定义子容器在父容器中的排列顺序,数值越小,排列越靠前,默认值为 0
    order: <integer>; /* 整数值,默认值是 0 */
    复制代码

    image.png

    flex-grow

    • grow 是扩展的意思,扩展的是子容器所占据的宽度,定义项目的放大比例,扩展所侵=占的空间就是除去元素外的剩余空白间隙
    • flex-grow 不支持负值,默认值是 0,表示不占用剩余的空白间隙扩展本身的宽度,若 flex-grow 大于0,则父容器剩余空间的分配就会发生,具体规则以下
      1. 全部剩余空间总量是 1
      2. 若只有一个子容器设置了 flex-grow 属性值
        1)若 flex-grow 值小于1,则扩展空间就是总剩余空间和这个比例的计算值
        2)若 flex-grow 值大于1,则独享全部剩余空间
      3. 如有多个子容器设置了 flex-grow 属性值
        1)若 flex-grow 值总和小于1,则每一个子容器扩展空间就是总剩余空间和当前元素设置的 flex-grow 比例的计算值
        2)若 flex-grow 值总和大于1,则全部剩余空间被利用,分配比例就是 flex-grow 属性值的比例,如全部的子容器都设置 flex-grow: 1,则表示剩余空白间隙你们等分;若设置的 flex-grow 比例是 1:2:1,则中间的子容器占据一半的空白间隙,剩下的先后两个元素等分
      4. 若当全部项目以 flex-basis 的值排列完后发现空间不够且 flex-wrap:nowrap 时,此时 flex-grow 则不起做用,就须要接下来的 flex-shrink 属性
    flex-grow: <number>; /* 数值,能够是小数,默认值是 0 */
    复制代码

    image.png

    flex-shrink

    • 要处理当父容器空间不足时,单个元素的收缩比例
    • flex-shrink 不支持负值,默认值是 1,即默认全部子容器都会收缩,若设置为 0,则表示不收缩,保持原始的 fit-content 宽度
    • flex-shrink 的内核跟 flex-grow 神似,flex-grow 是空间足够时如何利用空间,flex-shrink 则是空间不足时候如何收缩腾出空间
    • 此属性要生效,父容器的 flex-wrap 属性要设置为 nowrap
    • 若只有一个子容器设置了 flex-shrink
      • flex-shrink 值小于 1 则收缩的尺寸不彻底,会有一部份内容溢出父容器
      • flex-shrink 值大于等于1,则收缩彻底,正好填满父容器
    • 若多个子容器设置了 flex-shrink
      • flex-shrink 值的总和小于 1 则收缩的尺寸不彻底,每一个元素收缩尺寸占“彻底收缩的尺寸”的比例就是设置的 flex-shrink 的值
      • flex-shrink 值的总和大于 1 则收缩彻底,每一个元素收缩尺寸的比例和 flex-shrink 值的比例同样
      flex-shrink: <number>; /* 数值,默认值是 1 */
      复制代码
      image.png

    flex-basis

    • 定义了在分配多余空间以前,项目占据的主轴空间,浏览器根据这个属性,计算主轴是否有多余空间
    • 它能够设为跟 width 或 height 属性同样的值(如 350px),则项目将占据固定空间
    • 默认值:auto,项目原本的大小, item 的宽高取决于 width、height 值,没有设置 width、height 就按内容宽度来
    • 当主轴为水平方向且设置了 flex-basis 时,项目的 width 设置值会失效,flex 顾名思义就是弹性的意思,所以实际上不建议对 flex 子项使用 width 属性,由于不够弹性
    • 当剩余空间不足时,子容器的实际宽度一般不是设置的 flex-basis,由于 flex 布局剩余空间不足的时候默认会收缩
    • 当 flex-basis 值为 0 % 时,是把该项目视为零尺寸,即便声明该尺寸为 140px,也并无什么用
    • 当 flex-basis 值为 auto 时,则根据尺寸的设定值(如为 100px),则这 100px 不会归入剩余空间
    • flex-basis 需跟 flex-grow 和 flex-shrink 配合使用才能发挥效果
    flex-basis: <length> | auto; /* default auto */
    复制代码

    flex

    • flex 属性是 flex-grow、flex-shrink、flex-basis的缩写形式
    • 其中第 2 和第 3 个参数( flex-shrink 和 flex-basis )是可选的,默认值为0 1 auto
    • flex 默认值等同于 flex: 0 1 auto;
    • flex: none 等同于 flex: 0 0 auto;
    • flex: auto 等同于 flex: 1 1 auto;
    flex: none | auto | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
    
    // 当 flex 取值为一个非负数字,则该数字为 flex-grow 值,flex-shrink 取 1,flex-basis 取 0%
    // 以下同等
    .item {flex: 1;}
    .item {
         flex-grow: 1;
         flex-shrink: 1;
         flex-basis: 0%;
    }
    
    // 当 flex 取值为 0 时,对应的三个值分别为 0 1 0%
    item {flex: 0;}
    .item {
         flex-grow: 0;
         flex-shrink: 1;
         flex-basis: 0%;
    }
    
    // 当 flex 取值为一个长度或百分比,则视为 flex-basis 值,flex-grow 取 1,flex-shrink 取 1
    // 有以下等同状况(注意 0% 是一个百分比而不是一个非负数字)
    .item-1 {flex: 0%;}
    .item-1 {
         flex-grow: 1;
         flex-shrink: 1;
         flex-basis: 0%;
    }
    
    .item-2 {flex: 24px;}
    .item-2 {
         flex-grow: 1;
         flex-shrink: 1;
         flex-basis: 24px;
    }
    
    // 当 flex 取值为两个非负数字,则分别视为 flex-grow 和 flex-shrink 的值,flex-basis 取 0%,以下是等同的:
    .item {flex: 2 3;}
    .item {
         flex-grow: 2;
         flex-shrink: 3;
         flex-basis: 0%;
    }
    
    // 当 flex 取值为一个非负数字和一个长度或百分比,则分别视为 flex-grow 和 flex-basis 的值,flex-shrink 取 1,以下是等同的:
    .item {flex: 11 32px;}
    .item {
         flex-grow: 11;
         flex-shrink: 1;
         flex-basis: 32px;
    }
    复制代码

    align-self

    • align-self: align-self 指控制单独某个 flex 子项的垂直对齐方式,父容器上的 align-items 属性,表示子容器们,是全体;这里是 self,单独一个个体
    • 惟一区别就是 align-self 多了个auto(默认值),表示继承自 flex 容器的 align-items 属性值,其余属性含义如出一辙
  • 补充说明父容器的 flex-wrap 与子容器的 flex-shrink、flex-grow 之间的关系

    • 当 flex-wrap 为 wrap | wrap-reverse 且子容器宽度和不及父容器宽度时,flex-grow 会起做用,子容器会根据 flex-grow 设定的值放大(为 0 的项不放大)
    • 当 flex-wrap 为 wrap | wrap-reverse,且子容器宽度和超过父容器宽度时,首先必定会换行,换行后每一行的右端均可能会有剩余空间(最后一行包含的子项可能比前几行少则剩余空间可能会更大),这时 flex-grow 会起做用,若当前行全部子容器的 flex-grow 都为 0,则剩余空间保留;若当前行存在一个子容器的 flex-grow 不为 0,则剩余空间会被 flex-grow 不为 0 的子容器占据
    • 当 flex-wrap 为 nowrap,且子容器宽度和不及父容器宽度时,flex-grow 会起做用,子容器会根据 flex-grow 设定的值放大(为 0 的项不放大)
    • 当 flex-wrap 为 nowrap,且子容器宽度和超过父容器宽度时,flex-shrink 会起做用,子容器会根据 flex-shrink 设定的值进行缩小(为 0 的项不缩小)。但这里有个较为特殊状况,就是当这一行全部子容器 flex-shrink 都为 0时,即全部的子项都不能缩小,就会出现讨厌的横向滚动条
    • 总结上面四点,可看出无论在什么状况下,在同一时间 flex-shrink 和 flex-grow 只有一个能起做用,这其中的道理也很浅显:空间足够时 flex-grow 就有发挥的余地,而空间不足时 flex-shrink 就能起做用。固然 flex-wrap 的值为 wrap | wrap-reverse 时代表能够换行,既然能够换行,通常状况下空间就老是足够的,flex-shrink 固然就不会起做用

总结

flexbox 布局最适合应用程序的组件和小规模布局(一维布局),而 Grid 布局则适用于更大规模的布局(二维布局)

grid 布局

  • grid 布局又称为“网格布局”,能够实现二维布局,和以前的 table 布局差很少,同时还能够依赖于媒体查询根据不一样的上下文新定义布局(flex 布局虽然强大,可是只能是一维布局)
  • 和 table 布局不一样的是,grid 布局不须要在 HTML 中使用特定的标签布局,全部的布局都是在 CSS 中完成的,能够随意定义 grid 网格
  • 网格布局还能够摆脱如今布局中存在的文档流限制,即结构不须要根据设计稿从上往下布置了,这也意味着能够自由地更改页面元素位置,这最适合在不一样的断点位置实现最须要的布局,而再也不须要为响应设计而担忧 HTML 结构的问题

没有 HTML 结构的网格布局有助于使用流体、调整顺序等技术管理或更改布局。经过结合 CSS 的媒体查询属性,能够控制网格布局容器和它们的子元素,使用页面的布局根据不一样的设备和可用空间调整元素的显示风格与定位,而不须要去改变文档结构的本质内容

浏览器兼容性

具体可查阅:Can I use

image.png

grid 中一些概念

具体请看 CSS Grid布局:什么是网格布局

网格线(Grid Lines)

  • 网格线组成了网格,是网格的水平和垂直的分界线,一个网格线存在行或列的两侧,能够引用它的数目或定义的网格线名称

image.png

网格轨道(Grid Track)

  • 网格轨道就是相邻两条网格线之间的空间,就比如表格中行或列,全部在网格中的分为 grid column 和 grid row,每一个网格轨道可设置一个大小,用来控制宽度或高度

image.png

网格单元格(Grid Cell)

  • 网格单元格是指四条网格线之间的空间,因此它是最小的单位,就像表格中的单元格

image.png

网格区域(Grid Area)

  • 网格区域是由任意四条网格线组成的空间,因此可能包含一个或多个单元格,至关于表格中的合并单元格以后的区域

image.png

使用 grid 布局

  • 使用 grid 布局很简单,经过 display 属性设置属性值为 grid 或 inline-grid 或 subgrid(该元素父元素为网格,继承父元素的行和列的大小)
  • 网格容器中的全部子元素就会自动变成网格项目(grid item),而后设置列(grid-template-columns)和 行(grid-template-rows)的大小,设置 grid-template-columns 有多少个参数生成的 grid 列表就有多少个列,若没有设置 grid-template-columns,那默认只有一列,宽度为父元素的 100%
<div class="grid-container">
    <div class="item item1">1</div>
    <div class="item item2">2</div>
    <div class="item item3">3</div>
    <div class="item item4">4</div>
    <div class="item item5">5</div>
    <div class="item item6">6</div>
</div>

// 不设置 grid-template-columns,只设置 grid-template-row
.grid-container{
     display: grid;
     grid-template-rows: 50px 80px 100px;
     background: pink;
}
.item{
     border: 2px solid palegoldenrod;
     color: #fff;
     text-align: center;
     font-size: 20px;
}
复制代码

image.png

注:当元素设置了网格布局,column、float、clear、vertical-align 属性无效

  • 设置了 grid-template-columns 几个参数,就有几列(不超过 grid item 个数),设置的 grid-template-row 参数就是每一列的高度(超出列数的高度无效),虽然设置了四个 grid-template-rows,但由于只有两行,因此只有前两个值生效
.grid-container {
      padding: 20px;
      display: grid;
      grid-template-rows: 50px 100px 60px 80px;
      grid-template-columns: 50px 40px 100px 80px;
      background: pink;
}
.item {
      border: 2px solid palegoldenrod;
     color: #fff;
}
复制代码

image.png

  • 能够像 flex 同样设置每一列的宽度,使用了一个新的单位:fr

    CSS fr 单位是一个自适应单位,fr 单位被用于在一系列长度值中分配剩余空间,若已指定了多个部分,则剩下的空间根据各自的数字按比例分配
    fr 是基于网格容器可用空间来计算的(flex 也是同样),因此若是须要的话能够和其余单位混合使用

grid-template-columns: 1fr 1fr 2fr;
复制代码

image.png

行或列最小、最大尺寸

  • minmax() 函数建立行或列的最小最大尺寸,第一个参数定义网格轨道的最小值,第二个参数定义网格轨道的最大值,可接受任何长度值也接受 auto 值,auto 值容许网格轨道基于内容的尺寸拉伸或挤压
.grid-container {
     padding: 20px;
     display: grid;
     grid-template-rows: minmax(100px,200px) minmax(50px,200px);
     grid-template-columns: 1fr 1fr 2fr;
     background: pink;
     height: 300px;
}
复制代码
  • 上面将第一行高度设为 minmax(100px,200px),第二行高度设为 minmax(50px,200px),容器总高度设为 300px,这时每一列的高度要怎么算呢?
    • 先判断总高度是否小于第一列高度的最大值和第二列高度的最大值之和,若大于最大值之和,那第一列和第二列的高度都为设置的最大值,如果小于最小值之和,那第一列和第二列的高度都为设置的最小值
    • 而后这里就是先用 总高度 300px - 第一列最小高度 100px - 第二列最小高度 50px = 150px
    • 第一列高度:第一列最小高度 100px + 150px/2 = 175px;
    • 第二列高度:第二列最小高度 50px + 150px/2 = 125px;

image.png

重复行或者列

  • repeat() 属性可建立重复的网格轨道,这个适用于建立相等尺寸的网格项目和多个网格项目
  • repeat() 接受两个参数:第一个参数定义网格轨道应该重复的次数,第二个参数定义每一个轨道的尺寸
.grid-container{
    padding: 20px;
    display: grid;
    grid-template-columns: repeat(2,100px);
    grid-template-rows: repeat(3,100px);
    background: pink;
}
复制代码

image.png

间距

  • grid-column-gap:建立列与列之间的距离
  • grid-row-gap:行与行之间的距离
  • grid-gap: grid-row-gap 和 grid-column-gap 两个属性的缩写形式
.grid-container{
    padding: 20px;
    display: grid;
    grid-template-columns: repeat(2,100px);
    grid-template-rows: repeat(3,100px);
    grid-column-gap: 50px;
    grid-row-gap: 15px;
    background: pink;
}
复制代码

image.png

经过网格线定位 grid item

  • 能够经过表格线行或者列来定位 grid item
  • grid-row:grid-row-start 和 grid-row-end 的简写形式
  • grid-column:grid-column-start 和 grid-column-end 的简写形式
  • 若只提供一个值,指定了 grid-row-start 和 grid-column-start 的值
  • 若提供两个值,第一个值是 grid-row-start 或 grid-column-start 的值,第二个值是 grid-row-end 或 grid-column-end 的值,二者之间必需要用 / 隔开
grid-row: 2; 
grid-column: 3 / 4;
复制代码
  • 这四个值可用 grid-area 缩写,分别对应 grid-row-start、grid-column-start、grid-row-end、grid-column-end
grid-area: 2 / 2 / 3 / 3;
复制代码
.grid-container {
    padding: 20px;
    display: grid;
    grid-template-columns: repeat(2,100px);
    grid-template-rows: repeat(3,100px);
    grid-column-gap: 50px;
    grid-row-gap: 15px;
    background: pink;
}
.item {
    border: 2px solid palegoldenrod;
    color: #fff;
    text-align: center;
    font-size: 20px;
}
.item1 {
    grid-row-start: 2;
    grid-row-end: 3;
    grid-column-start: 2;
    grid-column-end: 3;
    background: #fffa90;
    color: #000;
}
复制代码

image.png

合并单元行与合并单元列

  • 这个和 excel 中的合并单元行/列是相同的(须要设置在 grid item 中)
  • 也可以使用 grid-row 和 grid-column 简写的形式,关键词 span 后面紧随数字,表示合并多少个列或行,/ 前面是从第几行/列开始
.grid-container {
      padding: 20px;
      display: grid;
      grid-template-columns: repeat(4,100px);
      grid-template-rows: repeat(3,100px);
      grid-column-gap: 50px;
      grid-row-gap: 15px;
      background: pink;

}
.item {
      border: 2px solid palegoldenrod;
      color: #fff;
      text-align: center;
      font-size: 20px;
}
.item1 {
      grid-column-start: 1;
      grid-column-end: 3;
      grid-row-start: 2;
      grid-row-end: 4;
}

// 或
// .item1 {
// grid-row: 2 / span 3; 
// grid-column: span 2;
//}
复制代码

image.png

自定义网格线名称

  • 在 grid 中,是能够自定义网格线的名称的,而后使用定义好的网格线来进行布局,[col1-start] 网格线名称必定要使用 [] 括住
  • 下图文字和辅助线是后台添加的

image.png

经过网格区域命名和定位网格项目

什么是网格区域

  • 网格区域(grid-area)是个逻辑空间,主要用来放置一个或多个网格单元格(Grid Cell),由四条网格线(Grid line)、网格区域每边一条、四边相交组织的网格轨道(Grid Track),网格区域是有四条网格线交织组成的网格空间,该空间中多是一个网格单元格也多是多个网格单元格

定义网格区域

  • 在 CSS Grid Layout 中定义网格区域有两种方式,一种是经过网格线来定义,另外一种是经过 grid-template-areas 来定义

网格线定义网格区域

  • 首先依赖于 grid-template-columns 和 grid-template-rows 显式定义网格线,甚至是由浏览器隐式建立网格线,而后经过 grid-area 属性经过取网格线,组成网格线交织区域,则该区域就是所讲的网格区域
  • 在使用 grid-area 属性调用网格线,遵循的规则是 grid-area: row-start/ column-start / row-end / column-end

grid-template-areas 定义网格区域

  • 在 CSS Grid Layout 中还能够经过 grid-template-areas 属性来定义网格区域的名称,而后须要放在对应网格区域的元素,能够经过 grid-area 属性来指定
  • 重复区域可使用同一个名称来实现跨区域,另外对于空的轨道区域可使用点号 . 来表明
<div class="grid-container">
    <div class="header ">header</div>
    <div class="content ">content</div>
    <div class="sidebar ">sidebar</div>
    <div class="footer ">footer</div>
</div>
.grid-container {
    text-align: center;
    padding: 20px;
   display: grid;
   grid-column-gap: 5px;
   grid-row-gap: 5px;
   background: pink;
   grid-template-areas: "header header header header header"
                        "sidebar content content content content"
                        "footer footer footer footer footer";
   grid-template-rows: 50px 150px 50px;
   grid-template-columns: 200px 200px 200px;
}
.header { grid-area:header; background: #fff}
.content { grid-area: content; background: #fffa90}
.sidebar { grid-area: sidebar; background: #5bc0de}
.footer { grid-area: footer; background: #ffff00}
复制代码

image.png

  • 这样布局有一个优势,在不设置高度的状况下(父容器和 grid-template-rows 的值,或 grid-template-rows 设置为 auto 时,slider 和 content 的高度是一致的,且会根据其内的高度自适应),如

image.png

rem 布局

什么是 rem

  • rem 和 em 很容易混淆,其实两个都是 CSS 的单位且也都是相对单位,先有的em 而后 CSS3 才引入的 rem,在介绍 rem 以前先来了解下 em

em 做为 font-size 的单位时,其表明父元素的字体大小,em 做为其余属性单位时,表明自身字体大小 —— MDN

  • em 可让页面更灵活、更健壮,比起处处写死的 px 值,em 彷佛更有张力,改动父元素的字体大小子元素会等比例变化,这一变化彷佛预示了无限可能
  • 有些人提出用 em 来作弹性布局页面,但其复杂的计算让人诟病,甚至有人专门作了个 px 和 em 的计算器,不一样节点像素值对应的 em 值

image.png

  • em 作弹性布局的缺点还在于牵一发而动全身,一旦某个节点的字体大小发生变化,则其后代元素都得从新计算
  • rem

rem 做用于非根元素时,相对于根元素字体大小;rem 做用于根元素字体大小时,相对于其出初始字体大小 —— MDN

/* 做用于根元素,相对于原始大小(16px),因此 html 的 font-size 为 32px*/
html {font-size: 2rem}

/* 做用于非根元素,相对于根元素字体大小,因此为 64px */
p {font-size: 2rem}
复制代码
  • rem 和 em 各有优势,尺有所短,寸有所长,我一直以为技术没有什么对错,只有适合不适合,我的以为 em 就是为字体和行高而生的,有时子元素字体就应该相对于父元素,元素行高就应该相对于字体大小;而 rem 的优势在于统一的参考系

rem 的布局原理

rem 布局的核心是设置好根 html 元素的 font-size,rem 布局的本质在我看来是等比缩放,通常是基于宽度

假设将屏幕宽度平均分红 100 份,每一份的宽度用 x 表示,x = 屏幕宽度 / 100,若将 x 做为单位,x 前面的数值就表明屏幕宽度的百分比。若想要页面元素随着屏幕宽度等比变化,须要上面的 x 单位,不幸的是 CSS 中并无这样的单位,幸运的是在 CSS 中有 rem,经过 rem 这个桥梁能够实现神奇的 x

div { width: 50x } /* 屏幕宽度的50% */
复制代码

若子元素设置 rem 单位的属性,经过更改 html 元素的字体大小就可以让子元素实际大小发生变化

html { font-size: 16px }
div { width: 2rem } /* 32px*/

html { font-size: 32px }
div { width: 2rem } /*64px*/
复制代码

若让 html 元素字体的大小恒等于屏幕宽度的 1/100,那 1rem 和 1x 就等价

html { fons-size: width / 100 }
div { width: 50rem } /* 50rem = 50x = 屏幕宽度的50% */
复制代码

如何让 html 字体大小一直等于屏幕宽度的百分之一呢?能够经过 js 来设置,通常须要在页面 dom ready、resize 和屏幕旋转中设置

document.documentElement.style.fontSize = document.documentElement.clientWidth / 100 + 'px';
复制代码

如何把 UE(UE 是用户体验(User Experience)的缩写,指用户界面功能图) 图中的获取的像素单位的值,转换为以 rem 为单位的值呢?公式元素宽度 / UE 图宽度 * 100,假设 UE 图尺寸是 640px,UE 图中的一个元素宽度是 100px,根据公式则是 100 / 640 * 100 = 15.625

div { width: 15.625rem }
复制代码

下面的表格是 UE 图等比缩放下元素的宽度 image.png 下面的表格是经过元素在不一样屏幕宽度下的计算值 image.png 上图能够看出 UE 图宽度和屏幕宽度相同时两边得出的元素宽度是一致的

上面的计算过程有些繁琐,能够经过预处理的 function 来简化过程,下面是 sass 的例子,less 相似

$ue-width: 640; /* ue图的宽度 */
@function px2rem($px) {
    @return #{$px/$ue-width*100}rem;
}

div {
    width: px2rem(100);
}
复制代码

其实有了 postcss 后,这个过程应该放到 postcss 中,源代码以下

div { width: 100px2rem }

// postcss 会对 px2rem 这个单位进行处理,处理后的结果以下
p { width: 15.625rem }
复制代码

在移动端中,通常为了防止在高清屏幕下像素不够用致使模糊,拿到的设计稿是 640px(iphone5 设备宽为320 px)或 750px 的两倍稿(iphone6 设备宽为375 px),按照设备宽度作了两倍的大小,那开发时在 CSS 中要设置什么尺寸呢?如何作到一份设计稿适配到不一样机型中?

  • 最佳方案是:在 photoshop 或其余工具中量出某个元素、图片、文字的尺寸,而后直接写到代码中,额外的适配不须要理会
    • 基于此,可以使用 SCSS 来提供一系列的基础支持
    • 为了便于计算,将页面分为 10 个块,根据映射关系只需计算某个元素在页面中占了多少块($rem),结合 html 中 font-size 的大小,就能在页面上设置好正确的元素大小
      width: px2rem(200);
      
      /* 移动端页面设计稿宽度 */
      $design-width: 750;
      /* 移动端页面设计稿 dpr 基准值 */
      $design-dpr: 2;
      /* 将移动端页面分为 10 块 */
      $blocks: 10;
      /* 缩放所支持的设备最小宽度 */
      $min-device-width: 320px;
      /* 缩放所支持的设备最大宽度 */
      $max-device-width: 540px;
      
      /* rem与px对应关系,1rem表明在JS中设置的html font-size值(为一块的宽度),$rem即为$px对应占多少块 $px $rem ------------- === ------------ $design-width $blocks */
      
      /* 单位px转化为rem */
      @function px2rem($px) {
          @return #{$px / $design-width * $blocks}rem;
      }
      
      /* 单位 rem 转化为 px,可用于根据rem单位快速计算原px */
      @function rem2px($rem) {
          @return #{$rem / $blocks * $design-width}px;
      }
      
      // 在对应的JS文件中
      // 在对应的JS文件中
      var docElem = document.documentElement,
          metaElem = document.querySelector('meta[name="viewport"]'),
          dpr = window.devicePixelRatio || 1,
          // 将页面分为 10 块
          blocks = 10,
          // 须要限制的最小宽度
          defaultMinWidth = 320,
          // 须要限制的最大宽度
          defaultMaxWidth = 540,
          // 计算的基准值
          calcMaxWidth = 9999999;
      
      // 设置docElem字体大小
      function setFontSize() {
          var clientWidth = docElem.clientWidth;
          clientWidth = Math.max(clientWidth, defaultMinWidth * dpr)
          // 调整计算基准值
          if (calcMaxWidth === defaultMaxWidth) {
              clientWidth = Math.min(clientWidth, defaultMaxWidth * dpr);
          }
          docElem.style.fontSize = clientWidth / blocks + 'px';
      }
      setFontSize();
      window.addEventListener(window.orientationchange ? 'orientationchange' : 'resize', setFontSize, false);
      复制代码

在 rem 布局中广泛采用 viewport scale 视窗缩放的方式,视窗缩放很简单,直接将 meta 标签中的 scale 进行更改,如 dpr 为 3 则 scale 以下。但缩放在某些安卓设备中支持度不太好,还须要作其余检测(检测了现用的一些机型,应该还不完整哈),同时将最终计算的 dpr 放到 html 中,供 css 作一些特殊适配

// 大部分dpr为2如下的安卓机型不识别scale,需设置不缩放
if (navigator.appVersion.match(/android/gi) && dpr <= 2) {
    dpr = 1;
}

setScale(dpr);
// 企业QQ设置了 scale 后,不能彻底识别 scale(此时 clientWidth 未收到缩放的影响而翻倍),需设置不缩放
if (navigator.appVersion.match(/qq\//gi) && docElem.clientWidth <= 360) {
    dpr = 1;
    setScale(dpr);
}
docElem.setAttribute('data-dpr', dpr);
// 设置缩放
function setScale(dpr) {
    metaElem.setAttribute('content', 'initial-scale=' + 1 / dpr + ',maximum-scale=' + 1 / dpr + ',minimum-scale=' + 1 / dpr + ',user-scalable=no');
}
复制代码

设置容器的最大最小宽度

  • 上面,随着拉伸,内容区愈来愈大,各元素尺寸也愈来愈大,已经进行了最小宽度的处理
  • 要控制缩放的程度,关键有两个点:尺寸计算基准、容器宽度(尺寸计算基准位于 meta 标签中的 data-content-max,容器宽度位于 body 标签中)
  • 在 JS 中进行匹配控制,需注意,由于已经进行了视窗的缩放,clientWidth 将会比设备宽度大,记得以 dpr 进行翻倍
  • 若仅仅限制计算基准值,容器不限制(将 body 标签中的属性去掉),就能够实现某种流式效果(另外一种方案)
<!DOCTYPE html>
<html>
    <head>
        <title>REM布局</title>
        <meta charset="utf-8">
        <meta lang="zh-CN">
        <meta name="viewport" data-content-max content="width=device-width,initial-scale=1,user-scalable=no">
        <link rel="stylesheet" href="./rem.css">
        <script src="./rem.js"></script>
    </head>

    <body data-content-max>
        <section class="container">

// js
// 须要限制的最小宽度
var defaultMinWidth = 320,
    // 须要限制的最大宽度
    defaultMaxWidth = 540,
    // 计算的基准值
    calcMaxWidth = 9999999;
if (metaElem.getAttribute('data-content-max') !== null) {
    calcMaxWidth = defaultMaxWidth;
}
...
// 设置docElem字体大小
function setFontSize() {
    var clientWidth = docElem.clientWidth;
    clientWidth = Math.max(clientWidth, defaultMinWidth * dpr)
    // 调整计算基准值
    if (calcMaxWidth === defaultMaxWidth) {
        clientWidth = Math.min(clientWidth, defaultMaxWidth * dpr);
    }

    docElem.style.fontSize = clientWidth / blocks + 'px';
}
// 在CSS中,简单地调用一下,核心方法已经抽离
html {
    @include root-width();
}
/* html根的宽度定义 */
@mixin root-width() {
    body {
        @include container-min-width();

        &[data-content-max] {
            @include container-max-width();
        }
    }

    /* 某些机型虽然设备dpr大于1,但识别不了scale缩放,这里须要从新设置最小宽度防止出现横向滚动条 */
    &[data-dpr="1"] body {
        min-width: $min-device-width;
    }
}
/* 设置容器拉伸的最小宽度 */
@mixin container-min-width() {
    margin-right: auto;
    margin-left: auto;
    min-width: $min-device-width;

    @media (-webkit-device-pixel-ratio: 2) {
        min-width: $min-device-width * 2;
    }

    @media (-webkit-device-pixel-ratio: 3) {
        min-width: $min-device-width * 3;
    }
}
/* 设置容器拉伸的最大宽度 */
// 要注意的是,这里的max-width也要配上dpr系数
@mixin container-max-width() {
    margin-right: auto;
    margin-left: auto;
    max-width: $max-device-width;

    @media (-webkit-device-pixel-ratio: 2) {
        max-width: $max-device-width * 2;
    }

    @media (-webkit-device-pixel-ratio: 3) {
        max-width: $max-device-width * 3;
    }
}
复制代码

文本大小是否用 rem 单位

  • 有时不但愿文本在 Retina 屏幕下变小,另外但愿在大屏手机上看到更多文本,以及如今绝大多数的字体文件都自带一些点阵尺寸,一般是 16px 和 24px,因此不但愿出现 13px 和 15px 这样的奇葩尺寸,能够选择使用 px 直接定义,若要求不严格也可直接使用 rem 单位
/* 设置字体大小,不使用rem单位, 根据dpr值分段调整 */
@mixin font-size($fontSize) {
    font-size: $fontSize / $design-dpr;

    [data-dpr="2"] & {
        font-size: $fontSize / $design-dpr * 2;
    }

    [data-dpr="3"] & {
        font-size: $fontSize / $design-dpr * 3;
    }
}
@include font-size(30px);
复制代码

rem 不是银弹

  • rem 是弹性布局的一种实现方式,弹性布局能够算做响应式布局的一种,但响应式布局不是弹性布局,弹性布局强调等比缩放,100% 还原;响应式布局强调不一样屏幕要有不一样的显示,好比媒体查询
  • 感受通常内容型的网站都不太适合使用 rem,由于大屏用户能够本身选择是要更大字体仍是要更多内容,一旦使用了 rem 就剥夺了用户的自由,如百度知道、百度经验都没有使用 rem 布局;一些偏向 app 类、图标类、图片类的,如淘宝、活动页面比较适合使用 rem,由于调大字体时并不能调大图标的大小
  • rem 能够作到 100% 的还原度,但同时 rem 的制做成本也更大,同时使用 rem 还有一些问题,下面一一列举
    • 首先是字体的问题,字体大小并不能使用 rem,因此字体大小不能使用 rem;因为设置了根元素字体的大小,会影响全部没有设置字体大小的元素,由于字体大小是会继承的,要每一个元素都显示设置的字体大小不太合理
    • 能够在 body 上作字体修正,如把 body 字体大小设置为 16px,但若用户本身设置了更大的字体,此时用户的设置将失效,好比合理的方式是将其设置为用户的默认字体大小
    html { fons-size: width / 100 }
    body { font-size: 16px }
    复制代码
    • 那字体的大小如何实现响应式呢?能够经过修改 body 字体的大小来实现,同时全部设置字体大小的地方都是用 em 单位,由于只有 em 才能实现同步变化,固然不一样屏幕字体大小相同也是很是合理和不错的效果,须要你本身作决策
    @media screen and (min-width: 320px) {
      body {font-size: 16px}
    }
    @media screen and (min-width: 481px) and (max-width:640px) {
      body {font-size: 18px}
    }
    @media screen and (min-width: 641px) {
      body {font-size: 20px}
    }
    
    div {font-size: 1.2em}
    div a {font-size: 1.2em}
    复制代码
    • 若是用户在 PC 端浏览时页面过宽怎么办?通常都会设置一个最大宽度,大于这个宽度的话页面居中,两边留白。设置 body 的宽度为 100rem,并水平居中
    var clientWidth = document.documentElement.clientWidth;
    clientWidth = clientWidth < 780 ? clientWidth : 780;
    document.documentElement.style.fontSize = clientWidth / 100 + 'px';
    
    body {
      margin: auto;
      width: 100rem
    }
    复制代码
    • 若是用户禁用了 js 怎么办?其实这种用户真很少了,要不放弃吧。。。
    // 首先能够添加noscript标签提示用户
    <noscript>开启JavaScript,得到更好的体验</noscript>
    
    // 给html添加一个320时的默认字体大小,保证页面能够显示
    html {fons-size: 3.2px}
    
    // 若是想要更好的体验,不如添加媒体查询吧
    @media screen and (min-width: 320px) {
      html {font-size: 3.2px}
    }
    @media screen and (min-width: 481px) and (max-width:640px) {
      html {font-size: 4.8px}
    }
    @media screen and (min-width: 641px) {
      html {font-size: 6.4px}
    }
    复制代码
  • rem 不是银弹,这个世上也没有银弹,每一个方案都有优势也有缺点,学会作出选择和妥协才是王道
  • rem 仅能作到内容的缩放,可是对于非矢量资源好比图片放大时的失真并没有法解决

rem 布局方案

vm 布局

  • CSS3 带来了 rem 的同时也带来了 vw 和 vh

vw:视口宽度的 1/100;vh:视口高度的 1/100 —— MDN

  • REM 布局中用到了 JS 来动态设置 html 的 font-size,可能形成页面的抖动
  • 可考虑比较新的 VW 布局,无需使用 JS,虽然说在移动端 iOS 8 以上以及 Android 4.4 以上才得到支持,不过仍是值得一用,若需兼容能够尝试 viewport-units-buggyfill
  • 有了 vw 彻底能够绕过 rem 这个中介了,下面两种方案是等价的
/* rem 方案 */
html { fons-size: width / 100 }
div { width: 15.625rem }

/* vw方案 */
div { width: 15.625vw }
复制代码
  • 在使用弹性布局时,通常会限制最小最大宽度,好比在 pc 端查看页面,此时 vw 就力不从心了,vw 是根据设备宽度进行计算的,因此没法设置容器最大最小宽度。由于除了 width 有 max-width,其余单位都没有,而 rem 能够经过控制 html 根元素的 font-size 最大值,而轻松解决这个问题

rem + vw 布局

  • 为了解决纯 vm 布局不能设置最大最小宽度的问题,引入 rem
  • 经过配置 html 根元素的 font-size 为 vw 单位,且配置最大最小的像素 px 值,在其余 CSS 代码中能够直接使用 rem 做为单位,调用超级简单
html {
    @include root-font-size();
}
line-height: px2rem(300);
复制代码
  • 而 scss 里面的实现,一样是先定义一个映射关系,将页面宽度进行分块(只是为了防止值太大)
  • 这里的 max-width 直接使用宽度值,由于使用的是 vw,视窗未缩放,而在页面标签(html 和 body)中,简单地配上属性表明是否须要限制宽度便可
  • 一样是计算基准值和容器宽度两个方面,若仅仅限制计算的基准值也能实现“流式效果”
html {
    @include root-font-size();
}
line-height: px2rem(300);
/* 移动端页面设计稿宽度 */
$design-width: 750;
/* 移动端页面设计稿dpr基准值 */
$design-dpr: 2;
/* 将移动端页面分为10块 */
$blocks: 10;
/* 缩放所支持的设备最小宽度 */
$min-device-width: 320px;
/* 缩放所支持的设备最大宽度 */
$max-device-width: 540px;
/* rem与px对应关系,1rem表明html font-size值(为一块的宽度),$rem即为$px对应占多少块 $px $rem ------------- === ------------ $design-width $blocks */
/* html 根元素的 font-size 定义,简单地将页面分为 $blocks 块,方便计算 */
@mixin root-font-size() {
    font-size: 100vw / $blocks;

    body {
        @include container-min-width();
    }

    /* 最小宽度定义 */
    @media screen and (max-width: $min-device-width) {
        font-size: $min-device-width / $blocks;
    }

    /* 最大宽度定义 */
    &[data-content-max] {
        body[data-content-max] {
            @include container-max-width();
        }

        @media screen and (min-width: $max-device-width) {
            font-size: $max-device-width / $blocks;
        }
    }
}
/* 设置容器拉伸的最小宽度 */
@mixin container-min-width() {
    margin-right: auto;
    margin-left: auto;
    min-width: $min-device-width;
}
/* 设置容器拉伸的最大宽度 */
@mixin container-max-width() {
    margin-right: auto;
    margin-left: auto;
    max-width: $max-device-width;
}

<!DOCTYPE html>
<html data-content-max>
    <head>
        <title>VW-REM布局</title>
        <meta charset="utf-8">
        <meta lang="zh-CN">
        <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
        <link rel="stylesheet" href="./vw-rem.css">
    </head>
复制代码

对比

  • 每一个方案都能保证在不一样机型下作到必定的适配
  • 通常来讲,可用直接考虑使用 REM 布局,因 REM 使用了 JS 动态设置 html 的 font-size
  • 但 scale 对安卓机型不太友好,要求极致的能够选用 VW
  • 纯 VW 布局不支持设置容器最大最小宽高,如须要此功能则选用 REM + VW布局
rem vm rem + vm
容器最小宽度 支持 不支持 支持
容器最大宽度 支持 不支持 支持
高清设备 1px 边框 支持 支持 支持
容器固定纵横比 支持 支持 支持
优势 一、rem 单位兼容性好
二、支持高清设备 1px 边框时可按以往方式直接写 1px
一、无需引入 js
二、vw 的用法比较规范
同 vm
缺点 一、使用 js 设置 html 的 font-size
二、使用 scale 来设置 viewport 缩放,以支持高清设备中的 1px,但 scale 在安卓机中的兼容性不太好
三、rem 的用法不太标准,带有一些 hack 的特变
一、vw 单位在老旧机型上不兼容
二、为了支持高清设备的 1px,使用比较复杂的 scss mixin,占用 :after 伪类,会产生较多的代码
同 vm

使用方式

  • 常规方式是引入公共基础代码,而后在业务代码中调用
  • 在 html 文件中能够配置 data-content-max 参数来限制最大最小宽度
  • 在 scss 基础部分还能够自定义这几个值(如果 REM 布局,修改这些值还须要在 rem.js 文件中同步修改)
/* 移动端页面设计稿宽度 */
$design-width: 750;
/* 移动端页面设计稿dpr基准值 */
$design-dpr: 2;
/* 将移动端页面分为10块 */
$blocks: 10;
/* 缩放所支持的设备最小宽度 */
$min-device-width: 320px;
/* 缩放所支持的设备最大宽度 */
$max-device-width: 540px;
复制代码
相关文章
相关标签/搜索