1 移动端布局
虽然 H5 的页面与PC的Web页面相比简单了不少,但让我们头痛的事情是要想尽办法让页面能适配众多不同的终端设备。看看下图你就会知道,这是多么痛苦的一件事情,看到这些数据,是否为此捏了一把汗出来。
1.1 基本概念
1.1.1 像素
物理像素
物理像素又被称为设备像素,是屏幕上显示数据的最基本的点,表示相对大小。不同分辨率下相同长度的 px 元素显示会不一样,是因为像素点的个数相同情况下,不同分辨率下每个像素点对应的像素宽度不同。比如同样是 14px 大小的字,在 1366×768 显示屏下会显示的小,在 1024×768 显示屏下会相对大。也称为物理像素(设备像素),是分辨率的尺寸单位。
CSS 像素
在不同屏幕上,CSS 像素呈现的物理尺寸一致,但 CSS 像素对应的物理像素具数不同。标准的显示密度下,1个 CSS 像素对应一个物理像素,缩放时,1个 CSS 像素对应的物理像素会减增。一般情况之下,CSS像素称为与设备无关的像素(device-independent pixel),简称DIPs。
屏幕密度
屏幕密度是指一个设备表面上存在的像素数量,它通常以每英寸有多少像素来计算(PPI)。
设备像素比
设备像素(device pixel ratio)比简称为dpr,其定义了物理像素和设备独立像素的对应关系。设备物理像素和设备独立像素比,即是指在理想布局宽度,使用多少个物理像素来渲染一个 CSS 像素。
设备像素比 = 物理像素 / 设备独立像素
iPhone6的设备宽度和高度为 375pt * 667pt,可以理解为设备的独立像素;而其dpr为2,根据上面公式,我们可以很轻松得知其物理像素为750pt * 1334pt。
如下图所示,某元素的CSS样式:
width: 2px;
height: 2px;
在不同的屏幕上,CSS 像素所呈现的物理尺寸是一致的,而不同的是 CSS 像素所对应的物理像素具数是不一致的。在普通屏幕下1个 CSS 像素对应1个物理像素,而在 Retina(视网膜屏,分辨率高,观感好) 屏幕下,1个CSS像素对应的却是4个物理像素。
注意:
-
屏幕是由一个一个发光的小点构成,这一个个的小点就是一个物理像素。分辨率为 1920 x 1080,指的是的屏幕中小点的数量
-
浏览器在显示网页时,需要将 CSS 像素转换为物理像素然后再呈现
-
一个css像素最终由几个物理像素显示,由浏览器决定:默认pc端,一个 CSS 像素 = 一个物理像素
1.1.2 视窗
视窗(viewport),简单的理解,viewport 是严格等于浏览器的窗口。在桌面浏览器中,viewport 就是浏览器窗口的宽度高度。但在移动端设备上就有点复杂。
<meta>
标签中定义了一些元数据信息,通过设置 <meta name="viewport">
,提供有关视口初始大小的信息,供移动设备使用。其主要用来告诉浏览器如何规范的渲染Web页面,而你则需要告诉它视窗有多大。
属性 | 属性值 | 描述 |
---|---|---|
width | 数值 / device-width | 视口宽度 |
height | 数值 / device-height | 视口高度 |
initial-scale | 0.0 ~ 10.0 | 设备宽度与视口大小之间的缩放比率 |
maximum-scale | 0.0 ~ 10.0 | 缩放最大值 |
minimum-scale | 0.0 ~ 10.0 | minimum-scale 0.0 ~ 10.0 |
user-scalable | 布尔值 | 默认 yes,为 no 时用户不能缩放网页 |
viewport-fit | contain/cover | 视窗填充屏幕的方式。默认值contain |
在开发移动端页面,我们需要设置meta标签如下:
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
代码以显示网页的屏幕宽度定义了视窗宽度。网页的比例和最大最小比例被设置为100%,不允许进行缩放,保证页面显示效果。
移动端涉及布局视口(Layout Viewport)、视觉视口(Visual ViewPort)和理想视口(Ideal ViewPort)。
-
布局视口
-
布局视口是指用视口元标签(viewport meta)来进行布局视口设置,CSS 布局是相对于布局视口计算
-
布局视口是网页布局的基准窗口,在 PC浏览器上,布局视口就等于当前浏览器的窗口大小
-
在移动端,布局视口被赋予一个默认值,大部分为 980px,这保证 PC的网页可以在手机浏览器上呈现,但是非常小,用户可以手动对网页进行放大
-
-
视觉视口是指用户当前看到的区域
-
视觉视口是指用户当前看到的区域
-
视觉视口默认等于当前浏览器的窗口大小(包括滚动条宽度)
-
-
理想视口是屏幕分辨率的值,通过设置
<meta name="viewport" content="width=device-width,initial-scale=1.0">
实现-
网站页面在移动端展示的理想大小
-
当页面不进行缩放时, CSS 像素=设备独立像素, 理想视口=视觉视口
-
viewport-fit 属性:
默认:
属性值为cover:
1.1.3 单位
-
em
-
rem:简单来说,rem就是相对于根元素
<html>
的font-size来做计算。而我们的方案中使用rem单位,是能轻易的根据<html>
的font-size计算出元素的盒模型大小。而这个特色对我们来说是特别的有益处。 -
vm/vh:vw(view-width),vh(view-height) 这两个单位是CSS新增的单位,表示视区宽度/高度,视区总宽度为100vw,总高度为100vh。
1.2 适配方案
1.2.1 百分比方案
viewport 的设置基本如下
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover" />
原理
使用百分比来设置宽度以及布局,高度视情况而定,例如:UI 设置的 360px 图里面有一个 100px 的区域,这个时候该区域宽度设置:
目标 width = (100 / 360) * 100 %
而且一般的情况下,我们都会用 max-width 和 min-width 做相关的宽度限制以防止过度的拉伸。以下是子元素设置百分比的参照值:
子元素 | 参照值 |
---|---|
width/height | 基于子元素的直接父元素,width相对于父元素的width,height相对于父元素的height |
top/bottom 和 left/right | 相对于直接非static定位的父元素的height/width |
padding/margin | 不论是垂直方向或者是水平方向,都相对于直接父亲元素的width,与父元素的height无关。 |
border-radius | 相对于自身的宽度 |
优点
原理简单,兼容性好,且掌握好参照值之后,在一定范围内基本不会出现适配的问题
缺点
如果屏幕尺度跨度太大,相对设计稿过大或者过小的屏幕不能正常显示,在大屏手机或横竖屏切换场景下可能会导致页面元素被拉伸变形,字体大小无法随屏幕大小发生变化。
设置盒模型的不同属性时,其百分比设置的参考元素不唯一,容易使布局问题变得复杂
开发过程中计算量大,且参照系容易选错,当屏幕跨度超过设计稿太多时,显示相较于原来设计稿会出现比较大的变形,并且如果不注意垂直方向的适配计算的话,容易出现长宽度与设计稿不一致,另外,background-position 参照于背景图片的像素, IE怪异模式对盒模型的解析不同等也是会影响参照值;
垂直方向的适配问题 !!!因为市面上的手机宽度比并不是一定的,千万别按照宽度来计算高度的百分比,而是应该按照高度来计算,其他参照参考以上表格
1.2.2 rem方案
原理
rem 是相对长度单位,rem方案中的样式设计为相对于根元素 font-size 计算值的倍数。根据 屏幕宽度 设置html 标签的 font-size,在布局时使用 rem 单位布局,达到自适应的目的,是 弹性布局 的一种实现方式。通过获取屏幕实际宽度动态的设置 html 的字体大小,1rem = html 的字体大小像素(font-size)。
rem计算方案
(function (doc, win) { let docEl = doc.documentElement, // 获取html resizeEvt = "orientationchange" in window ? "orientationchange" : "resize", width = 750, // 设计稿宽,用时只需要修改这一处 recalc = function () { const clientWidth = docEl.clientWidth; // 获取设备尺寸 if (!clientWidth) return; // 如果没有值,回去 if (clientWidth > width) { // 如果超过设计稿宽度,就给一个固定值 docEl.style.fontSize = "100px"; docEl.style.width = width + "px"; docEl.style.margin = "0 auto"; } else { docEl.style.fontSize = 100 * (clientWidth / width) + "px"; docEl.style.width = ""; docEl.style.margin = ""; } }; if (!doc.addEventListener) return; // 如果没有这个方法,回去 win.addEventListener(resizeEvt, recalc, false); // 改变大小时调整一下 doc.addEventListener("DOMContentLoaded", recalc, false); // 加载完成时调整 })(document, window); // 使用时:1rem = 设计稿的宽度 / 100
<script> (function (doc, win) { let docEl = doc.documentElement, // 获取html resizeEvt = "orientationchange" in window ? "orientationchange" : "resize", width = 750, // 设计稿宽,用时只需要修改这一处 recalc = function () { const clientWidth = docEl.clientWidth; // 获取设备尺寸 if (!clientWidth) return; // 如果没有值,回去 if (clientWidth > width) { // 如果超过设计稿宽度,就给一个固定值 docEl.style.fontSize = "100px"; // docEl.style.width = width + "px"; // docEl.style.margin = "0 auto"; } else { docEl.style.fontSize = 100 * (clientWidth / width) + "px"; // docEl.style.width = ""; // docEl.style.margin = ""; } }; if (!doc.addEventListener) return; // 如果没有这个方法,回去 win.addEventListener(resizeEvt, recalc, false); // 改变大小时调整一下 doc.addEventListener("DOMContentLoaded", recalc, false); // 加载完成时调整 })(document, window); // 使用时:1rem = 设计稿的宽度 / 100 </script>
优点
兼容性好
ios: 6.1 系统以上都支持
android: 2.1 系统以上都支持
大部分主流浏览器都支持
相较于之前的静态布局和百分比方案,页面不会因为伸缩发生变形,自适应效果更佳。
开发计算量小,参照系单一
缺点
不是纯 CSS 移动适配方案,需要引入js脚本,在头部内嵌一段 js脚本,监听分辨率的变化来动态改变根元素的字体大小,css样式和 js 代码有一定 耦合性,并且必须将改变 font-size 的代码放在 css 样式之前。
小数像素问题,浏览器渲染最小的单位是像素,元素根据屏幕宽度自适应,通过 rem 计算后可能会出现小数像素,浏览器会对这部分小数四舍五入,按照整数渲染。浏览器在渲染时所做的摄入处理只是应用在元素的尺寸渲染上,其真实占据的空间依旧是原始大小。也就是说如果一个元素尺寸是 0.625px,那么其渲染尺寸应该是 1px,空出的 0.375px 空间由其临近的元素填充;同样道理,如果一个元素尺寸是 0.375px,其渲染尺寸就应该是0,但是其会占据临近元素 0.375px 的空间。会导致:缩放到低于1px的元素时隐时现(解决办法:指定最小转换像素,对于比较小的像素,不转换为 rem 或 vw);两个同样宽度的元素因为各自周围的元素宽度不同,导致两元素相差1px;宽高相同的正方形,长宽不等了;border-radius: 50% 画的圆不圆。
Android 浏览器下 line-height 垂直居中偏离的问题。常用的垂直居中方式就是使用line-height,这种方法在Android设备下并不能完全居中。最佳推荐:利用弹性盒布局的居中方案可以完美解决该问题
淘宝适配方案
<script>!function(e,t){var n=t.documentElement,d=e.devicePixelRatio||1;function i(){var e=n.clientWidth/3.75;n.style.fontSize=e+"px"}if(function e(){t.body?t.body.style.fontSize="16px":t.addEventListener("DOMContentLoaded",e)}(),i(),e.addEventListener("resize",i),e.addEventListener("pageshow",function(e){e.persisted&&i()}),2<=d){var o=t.createElement("body"),a=t.createElement("div");a.style.border=".5px solid transparent",o.appendChild(a),n.appendChild(o),1===a.offsetHeight&&n.classList.add("hairlines"),n.removeChild(o)}}(window,document)</script>
1.2.3 vh/vw方案
原理
vh,vw 主要是 css3 中新出的为了进行移动适配的长度单位:主要是相对于视口viewport的百分比
视口是浏览器中用于呈现网页的区域,移动端的视口通常指的是布局视口
vw : 1vw 等于视口宽度的 1%
vh : 1vh 等于视口高度的 1%
vmin : 选取 vw 和 vh 中最小的那个
vmax : 选取 vw 和 vh 中最大的那个
计算方式实际上与百分基本一致,不过多了一个优势,就是参照系没有百分比那么复杂
优点
纯 CSS 移动端适配方案,不存在脚本依赖问题
相对于 rem 以根元素字体大小的倍数定义元素大小,逻辑清晰简单,视口单位依赖于视口的尺寸 "1vw = 1/100 viewport width",根据 视口尺寸的百分比来定义元素宽度
缺点
存在一些兼容性问题,Android 4.4 以下不支持
px 转换成 vw 不一定能完全整除,因此有一定的像素差
<style> html { font-size: 5.3333vw; } .box1 { width: 18.75rem; height: 0.875rem; background-color: #bfa; } </style> <div class="box1"></div>
1.2.4 rem+vw/vh方案
原理
vw/vh 方案能够实现宽度和高度的自适应,并且逻辑清晰,由于其被支持得较晚,所以存在一定的兼容性问题。将 vw/vh 方案与 rem 方案相结合,给根元素设置随视口变化的vw单位。
对于 1080px 宽的设计稿,设置默认根字号的大小为 100px,那么设计稿中 1px 对应的是 100vw/1080 = 0.0925926vw,并且 1rem = 100px,也就可以得到 1rem = 9.256926vw
同时可以使用媒体查询限制根元素的最大最小值,实现对页面的最大最小宽度限制,对用户的视觉体验更好。
实用性
rem 弹性布局方式作为移动端 web 页面适配方法,后期从 rem 过渡到 vw ,只需要通过 改变根元素大小的计算方式 不需要其他处理。vw 将会成为一种更好的适配方式,目前由于兼容性的原因得不到广泛应用。rem+vw/vh 不存在 vw/vh 的兼容性问题,可以成为由 rem 向 vw/vh 转变的一种过渡方案。
1.2.5 Flex弹性布局
随着 HTML5 和 CSS3 给我们带来了弹性盒布局,移动端开发的很多问题基本也变得 so easy。
原理
弹性布局,用来为盒装模型提供最大的灵活性,任何一个容器都可以指定为flex布局。采用flex布局的元素,被称为容器,它的所有子元素会自动成为容器成员,称为flex项目
优点
纯粹的 CSS+HTML 解决方案,父元素 display:flex,子元素配合设置布局,排列方向,排列方式,分布方式等即可
html 的结构清晰。需要更少的层级
特别是在移动端,良好的 align 与 justify 居中可以完美替换以前还需要:定位加 margin, margin: auto 等各种复杂的居中解决方案
缺点
对于低端老年机,不支持 flex 属性的不太友好
1.2.6 媒体查询
原理
主要实现是通过媒体查询,通过给不同分辨率的设备编写不同的样式实现响应式布局,用于解决不同设备不同分辨率之间兼容问题,一般是指PC、平板、手机设备之间较大的分辨率差异。实现上不局限于具体的方案,通常结合了 流式布局 + 弹性布局 方案。比如给小屏幕手机设置 @2x 图,为大屏手机设置 @3x 图
苏宁适配方案(rem+媒体查询)
html { font-size: 50px } body { font-size: 24px } @media screen and (min-width:320px) { html { font-size: 21.33px } body { font-size: 12px } } @media screen and (min-width:360px) { html { font-size: 24px } body { font-size: 12px } } @media screen and (min-width:375px) { html { font-size: 25px } body { font-size: 12px } } @media screen and (min-width:384px) { html { font-size: 25.6px } body { font-size: 14px } } @media screen and (min-width:400px) { html { font-size: 26.67px } body { font-size: 14px } } @media screen and (min-width:414px) { html { font-size: 27.6px } body { font-size: 14px } } @media screen and (min-width:424px) { html { font-size: 28.27px } body { font-size: 14px } } @media screen and (min-width:480px) { html { font-size: 32px } body { font-size: 15.36px } } @media screen and (min-width:540px) { html { font-size: 36px } body { font-size: 17.28px } } @media screen and (min-width:720px) { html { font-size: 48px } body { font-size: 23.04px } } @media screen and (min-width:750px) { html { font-size: 50px } body { font-size: 24px } }
优点
不同屏幕与设备精确适配达到最好的用户友好度
对一些布局紧凑的页面和活动页的适配是最友好的方式,没有之一
PC,移动只需要一套代码
缺点
要匹配足够多的设备与屏幕,一个web页面需要多个设计方案,工作量比较大
通过媒体查询技术需要设置一定量的断点,到达某个断点前后的页面发生显著变化,用户体验不太友好
页面中上存在一定的“冗余”代码
最致命的还是由于PC和移动的交互方式和用户行为存在差距,不仅仅需要 CSS 适配,连一下交互方式也可能需要俩套 JS 来适配。
1.2.7 总结
-
首先需要明确一点的是:并不存在某一个方案完美适用于移动端的所有场景,也并不是每一个方案到现在已经毫无用处
-
首推的布局方案为:弹性盒布局(如果实在不嫌麻烦大的分块布局使用 vw,vh 也可以,甚至使用 rem 也行)
-
对于上述的各种移动端web页面自适应方案来说,都存在着一些优势和不足
-
国内的一些互联网站,通过查看网页源代码发现,它可能不是某一种方案的单独使用,而是几种方案的结合
-
一个页面上,元素的宽度设置上有百分比,也有 rem,字体的样式中有 rem,有 em,也有固定大小的 px;在屏幕宽度过大时不再缩放,也会用到媒体查询,并且响应式设计更多地可能是针对不同设备间的自适应
-
对于移动端web页面的自适应方案来说,现在用的比较多的是rem,逐渐向 vw/vh 发展,而 rem+vw/vh 则是作为 vw/vh 向后兼容的一种过渡
-
响应式针对的是不同分辨率设备而进行的适配式设计,以利用 @media 规则为主要手段,而自适应则忽略@media 以比例布局为主,目的是适应不同的浏览器窗口大小
对图片的处理
-
指定图像宽度时使用相对单位,防止意外溢出视口,如 width: 50%,将图片宽度设置为包含元素宽度的 50%
-
因为 css 允许内容溢出容器, 需要使用 max-width: 100% 来保证图像及其他内容不会溢出
-
维护自适应页面中图片宽高比固定比较常用的方法是使用 padding 设置
1.2.8 布局方案
rem、vw/vh、百分比以及弹性盒做整体的布局:
-
当内容密集时考虑利用 媒介查询 来针对密集区域的内容来实现不同宽度的类似 icon、字体、margin,padding 小边距的设置(px)这样就不会因为适配而使得间距变小甚至挤到一起这种不太好的体验,实际上现在淘宝、京东这些都是这套解决方案
-
当内容不密集时就可以使用 rem, vw,vh 来设置 px 来设置即可
对于一些 UI 场景设计出来小于浏览器设置的 12px 像素
2 移动端页面制作规范
2.1 计量单位的使用
CSS 的计量单位选择
px:固定的像素值
em:相对父级元素的 font-size 设置来作为当前元素 1em 所代表的像素值,如父节点的 font-size:10px,当前节点的 font-size:1.2em,则当前节点的 font-size 实为 12px
rem:相对根节点 html 的 font-size 设置来作为当前元素 1rem 所代表的像素值,与 em 的区别就是 rem 的基本度量单位与父节点无关,只与根节点 font-size 的设置有关,如设置 html{font-size:10px;} 后当前 dom 所有节点的 1rem 都表示 10px
vm/vh:表示视区宽度/高度,视区总宽度为 100vw,总高度为 100vh
移动端开发中我们使用 rem 作为基本计量单位,同时将根节点默认字号大小设为 font-size:62.5%,因移动端浏览器默认字号大小为 16px,16*62.5% 刚好为 10px
html { /* 相当于 10px */ font-size: 62.5%; } /* #example 的字体大小为 12px*/ #example { font-size: 1.2rem } /* #example 子节点 div 的字体大小为 14px;宽度为 100px;高度 100px */ #example div { font-size: 1.4rem; width: 10rem; height: 10rem }
-
安卓下
<textarea>
标签的内容字体大小不支持 rem 设置
2.2 移动端开发细节和优化
在移动端使用新的 CSS3 样式代替原来在 PC 上的开发习惯
-
在宽度为100%的布局中,实现横向并排元素宽度的自动伸缩以及水平垂直居中平均分布、首尾分布排列等考虑使用 flex 布局
-
垂直居中使用 flex 实现垂直居中。
-
尽量使用 border-radius,box-shadow,text-shadow 等 CSS3 样式实现诸如圆角、渐变色、盒子投影、字体投影等,减少使用图片
-
对于单色的 icon 图标,我们将会整理出一套常用图标,并制作成字体,利用 CSS3 的@font-face使用自定义字体导入,这样的话,可以像修改字体一样随意地修改图标的颜色、大小、背景色、特殊效果(如投影)等,而不再需要每一种颜色就需要切一份图片
-
利用 transform:rotate(90deg) 来获取旋转了不同角度的 icon,避免每个角度需要切一张图片
-
在动画中,利用 CSS3 动画属性如 transform:translate(10px,12px) 来改变元素的偏移位置,减少使用left和top来做位移动画
3 响应式与自适应
响应式针对的是不同分辨率设备而进行的适配式设计,以利用@media规则为主要手段
网页可以根据不通的设备或窗口大小呈现出不同的效果
使用响应式布局,可以使一个网页适用于所有设备
响应布局的关键就是媒体查询
通过媒体查询,可以为不通的设备,或设备不同状态来分别设置样式
自适应则忽略@media以比例布局为主,目的是适应不同的浏览器窗口大小
现今大型网站,例如说淘宝网,已经没有做响应式了。
我们会发现,淘宝网手机端和网页端使用的是两个域名,也就是说,不同的客户端已经不再共用一套dom结构了。而是区分开来做自适应。然后每次用户访问的时候它就根据客户端的类型重定向。
为什么呢?
试想一下淘宝这种大型网站,一个分页下的商品条目特别多,并且每个商品条目的dom结构又十分复杂,而且pc端往往显示的信息是要比手机端更多的。
如果不分开做两套,而是直接用响应式的话,那么pc端上显示的很多dom就要在手机端上隐藏,结果这些dom都没有被用到,但是却加载了。
在这个流量和速度至上的时代,代码冗余先不说,多加载的这些无用的代码而消耗的流量,从某种意义上来说就已经损失了很多的效益。
4 图片模糊处理
理论上,1个位图像素对应于1个物理像素,图片才能得到完美清晰的展示。
对于 dpr=2 的 Retina 屏幕而言,1个位图像素对应于4个物理像素,由于单个位图像素不可以再进一步分割,所以导致图片看起来比较模糊。
对于图片模糊问题,比较好的方案就是用多倍图片(@2x)。
如:一个200×300(CSS pixel)的img标签,对于dpr=2的屏幕,用400×600的图片,如此一来,位图像素点个数就是原来的4倍,在Retina屏幕下,位图像素点个数就可以跟物理像素点个数形成 1 : 1的比例,图片自然就清晰了。
4.1 多倍图
为了使图片在移动端中正常清晰的显示。
叫多倍图的原因就是不同的移动设备屏幕分辨率不同。比如说:二倍图、三倍图、四倍图等,这些就是多倍图。这里介绍一下二倍图,其他的多倍图都是同样的原理。
4.1.1 二倍图
直接使用普通图片和使用二倍图呈现的图片的清晰度区别。
直接使用原图片直接丢到移动端页面的,可以很明显的看出图片的边缘有锯齿状。
进行压缩通过二倍图方式显示的图片,相比之下则清晰了很多。这就是多倍图的效果。