移动端web页面的开发,由于手机屏幕尺寸、分辨率不同,或者需要考虑横竖屏问题,为了使得web页面在不同移动设备上具有相适应的展示效果,需要在开发过程中使用合理的适配方案来解决这个问题。经过整理,常用方案有以下几种
视口相关理解:
获取布局视口的宽度:document.documentElement.clientWidth
获取设备css像素(屏幕尺寸):window.screen.width
<meta name=”viewport” content=”width=device-width, initial-scale=1, maximum-scale=1″>
width:控制 layout viewport 的大小,可以指定的一个值,如 600,或者特殊的值,如 device-width 为设备的宽度(单位为缩放为 100% 时的 CSS 的像素)。
方案一:百分比—固定高度,宽度自适应
这种方案是目前使用较多的方案,也是相对较简单的实现方案:
该方法使用了理想视口:
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
垂直方向使用固定的值,水平方向使用弹性布局,元素采用定值、百分比、flex布局等。这种方案相对简单,还原度也非常低。
使用 百分比% 定义 宽度,高度 用px固定,根据可视区域实时尺寸进行调整,尽可能适应各种分辨率,通常使用max-width/min-width控制尺寸范围过大或者过小。下表是子元素不同属性设置百分比的依据
属性 | 设置参考 |
---|---|
height/width | 基于子元素的直接父元素,width相对于父元素的width,height相对于父元素的height |
top/bottom 和left/right | 相对于直接非static定位的父元素的height/width |
padding/margin | 不论是垂直方向或者是水平方向,都相对于直接父亲元素的width,与父元素的height无关 |
border-radius | 相对于自身的宽度 |
方案二:rem—固定布局视口,以rem作为宽度单位,根据不同屏幕动态写入font-size
实例:网易新闻
固定布局视口,设置理想视口
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
以640px设计稿和750px的视觉稿,网易这样处理的:DOMContentLoaded后,设置rem
var width = document.documentElement.clientWidth; // 屏幕的布局视口宽度
var rem = width / 7.5; // 750px设计稿与375px的屏幕都将布局视口分为7.5份,
// 如果设计稿是640px
var rem = width / 6.4; // 640px设计稿与320px的屏幕都将布局视口分为6.4份
这里width除以的常数值主要是为了得到100,方便根据设计稿计算元素的尺寸。这样不管是750px设计稿还是640px设计稿,1rem 等于设计稿上的100px。故px转换rem时:
rem = px / 100
在750px设计稿上:
//75px 对应 0.75rem, 距离占设计稿的10%;
//在ipone6上:
width = document.documentElement.clientWidth = 375px;
rem = 375px / 7.5 = 50px;
0.75rem = 37.5px; //(37.5/375=10%;占屏幕10%)
//在ipone5上:
width = document.documentElement.clientWidth = 320px;
rem = 320px / 7.5 = 42.667px;
0.75rem = 32px; //(32/320=10%;占屏幕10%)
故对于设计稿上任何一个尺寸换成rem后,在任何屏下对应的尺寸占屏幕宽度的百分比相同。故这种布局可以百分比还原设计图。
此方案的font-size使用了rem作为单位
js代码:
(function (doc,win) {
var fn = function () {
var deviceWidth = doc.documentElement.clientWidth; // 获取的是布局视口宽度
// 移动设备的横向逻辑像素超过640,可以去访问pc端了。
if (deviceWidth > 640) { // 保持font-size值不变。
deviceWidth = 640;
}
doc.documentElement.style.fontSize = deviceWidth / 7.5 + 'px';
}
window.addEventListener('orientationchange'in win ? 'orientationchange' : 'resize', fn ,false);
doc.addEventListener('DOMContentLoaded',fn,false);
})(document,window)
方案三:rem—动态缩放布局视口,以rem作为宽度单位,动态写入font-size
实例:手机淘宝
以iphone6的设计稿750为例
根据dpr来设置viewport缩放。看看淘宝的:
var devicePixelRatio = window.devicePixelRatio;
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
var dpr,scale;
if (isIPhone) {
if (devicePixelRatio >=3) {
dpr = 3;
} else if (devicePixelRatio >=2) {
dpr = 2;
} else {
dpr = 1;
}
} else {
dpr = 1;
}
scale = 1 / dpr;
// 设置meta标签
doc.write("<meta name=viewport content=width=device-width,initial-scale="+scale+",minimum-scale="+scale+",maximum-scale="+scale+",user-scalable=no>");
淘宝只对iphone做了缩放处理,对于android所有dpr=1,scale=1即没有缩放处理。(淘宝的flexible适配方案为什么只对iOS进行dpr判断,对于Android始终认为其dpr为1?)
缩放之后,布局视口的宽度 = device-width / scale
将屏幕分为固定的块数7.5,这个数值主要是为了得出容易计算的rem:
var width = document.documentElement.clientWidth; // 屏幕的布局视口宽度
var rem = width / 7.5; // 将布局视口分为7.5份
这样在任何屏幕下,总长度都为7.5rem。1rem对应的值也不固定,与屏幕的布局视口宽度有关。
此方案与方案二相似,只是对iphone做了viewport缩放,能百分比还原设计稿。
相对完整代码:
(function (doc,win) {
var devicePixelRatio = window.devicePixelRatio;
// 只对iphone的视口进行缩放
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
var dpr,scale;
if (isIPhone) {
if (devicePixelRatio >=3) {
dpr = 3;
} else if (devicePixelRatio >=2) {
dpr = 2;
} else {
dpr = 1;
}
} else {
dpr = 1;
}
scale = 1 / dpr;
// 设置meta标签
doc.write("<meta name=viewport content=width=device-width,initial-scale="+scale+",minimum-scale="+scale+",maximum-scale="+scale+",user-scalable=no>");
// 动态设置html字体大小
var fn = function () {
var deviceWidth = doc.documentElement.clientWidth; // 获取的是布局视口宽度
// 移动设备的横向逻辑像素超过640(这时候物理像素大于1280),可以去访问pc端了。
if (deviceWidth > 1280) { // 保持font-size值不变。
deviceWidth = 1280;
}
doc.documentElement.style.fontSize = deviceWidth / 7.5 + 'px';
}
window.addEventListener('orientationchange'in win ? 'orientationchange' : 'resize', fn ,false);
doc.addEventListener('DOMContentLoaded',fn,false);
})(document,window)
方案四:vh/vw
原理
视口是浏览器中用于呈现网页的区域,移动端的视口通常指的是 布局视口
- vw : 1vw 等于 视口宽度 的 1%
- vh : 1vh 等于 视口高度 的 1%
- vmin : 选取 vw 和 vh 中 最小 的那个
- vmax : 选取 vw 和 vh 中 最大 的那个
使用 css 预处理器把设计稿尺寸转换为 vw 单位,包括 文本,布局高宽,间距 等,使得这些元素能够随视口大小自适应调整。以1080px设计稿为基准,转化的计算表示为
// 以1080px作为设计稿基准
$vw_base: 1080
@function vw($px) {
@return($px / 1080) * 100vw
}
方案五:vw + rem
原理:通过vw这一动态单位来动态设置rem(根元素html的font-size),从而达到不同设备的设配
此方法使得任何页面在不同分辨率设备,所显示的内容均为一样,相当于等比例缩放。首先我们来看一下vw的兼容
步骤:
(1)设置理想视口
html页面设置meta标签(该标签在做移动端页面时不用说肯定都要加上,如果引用的开源代码可能会自动加上该标签,则不用手动添加)
<meta name = 'viewport' content = 'width = device-width,initial-scale = 1,minimum-scale = 1, maximum-scale = 1, user-scalable = no'/>
(2)设置html元素的font-size属性
html {
font-size: calc(16 / 3.75 * 1vw);
}
解释一下公式里的参数:
16 : 表示浏览器默认字体大小
3.75 : 目前大多数设计图分别有两个尺寸,640或者750,可能750占大多数,那我们就拿750尺寸作为参照,这样 3.75px(css像素) == 1vw
以上公式则是将750尺寸设计图下浏览器的rem设置为了16px。公式里的数值可以根据设计稿以及想要设置的rem大小进行改变,同时需注意浏览器对于最小字体的限制。
通过以上公式,就将rem转换为了根据设备变化的动态单位。
(3)现在我们就可以根据rem来进行眼样式的编写。但是在对照设计稿转换成rem时涉及换算比较麻烦,这个时候我们可以考虑css预处理器的函数,以下为scss里面的函数参考
/* 1. 新建px转换为rem方法*/
@function px2rem($px){
@return $px / 16 + rem;
}
/*2. 使用该方法 这里效果图里面是多少尺寸,就写多少尺寸,比如某元素字体大小为24px*/
.aaa{
font-size: px2rem(24);
}
/*某div高度为100px*/
.bbb{
height: px2rem(100);
}
函数中的具体数值视情况而定
至此,vw+rem适配法已经完成。
移动端web页面适配方案中的通用问题
1px问题
在不同的移动设备中,css像素的1px因为dpr的不同会使用dpr个物理像素来显示,这就造成1px在不同设备上显示的粗细不一致,为解决这个问题,可采用如下方案:
伪元素+transform: scale(0.5) 方案
给需要设置1px边框的元素添加border-1px类
.border-1px:after {
content: '';
position: absolute;
top: 0;
left: 0;
border: 1px solid red;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
/* 2倍屏 */
@media screen and (min-device-pixel-ratio: 2), (-webkit-min-device-pixel-ratio: 2){
border-radius: 20px; // 圆角根据dpr放大相应的倍数
width: 200%;
height: 200%;
-webkit-transform: scale(0.5);
transform: scale(0.5);
}
/* 3倍屏 */
@media screen and (min-device-pixel-ratio: 3), (-webkit-min-device-pixel-ratio: 3){
border-radius: 30px; // 圆角根据dpr放大相应的倍数
width: 300%;
height: 300%;
-webkit-transform: scale(0.33);
transform: scale(0.33);
}
}
还可以写成mixin的形式,见移动端设置border的1px像素解决方案
1px问题还可以通过viewport缩放来解决,如方案三。viewport根据dpr缩放后,css中的1px就是用的一个物理像素来表示。
2倍图3倍图问题
移动端开发过程中,因为手机的dpr(设备像素比不同),需要根据dpr来修改图标的大小,判断使用@2x 图 还是 @3x 图,解决高清的适配。
css3的 -webkit-min-device-pixel-ratio属性+@media媒体查询(只能用于背景图片)
/*less,sass这类css预处理语言中的混合,可以理解成自定义了一段代码,后面可以用@include调用*/
@mixin bg-image($url) {
background-image: url($url + "@2x.png");
@media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3){
background-image: url($url + "@3x.png");
}
}
/*用@include调用*/
div{
width:30px;
height:20px;
background-size:30px 20px;
background-repeat:no-repeat;
@include bg-image('../../../../static/image/map_loading');
}
img标签的选择
对于引入的图片,如果想要图片适应不同像素密度的屏幕,并且屏幕上显示图片的实际尺寸相同,使用srcset属性用来指定多张图像。它的值是一个逗号分隔的字符串,每个部分都是一张图像的 URL,后面接一个空格,后接是像素密度描述符。浏览器根据当前设备的像素密度,选择需要加载的图像。如果srcset属性都不满足条件,那么就加载src属性指定的默认图像。
<img srcset="foo-320w.jpg,
foo-480w.jpg 1.5x,
foo-640w.jpg 2x"
src="foo-640w.jpg">
<!--srcset属性给出了三个图像URL,适应三种不同的像素密度, 后面的像素密度描述符,格式是像素密度倍数 + 字母x。1x表示单倍像素密度,可以省略。-->
如果想要针对不同屏幕,使用不同分辨率版本和尺寸的图片,使用属性srcse 和 sizes 。srcset 定义了允许浏览器选择的图像集,以及每个图像的大小(使用w单位)。sizes定义了一组媒体条件(例如屏幕宽度),指明当某些媒体条件为真时,什么样的图片尺寸是最佳选择。
<img srcset = "elva-fairy-320w.jpg 320w,
elva-fairy-480w.jpg 480w,
elva-fairy-800w.jpg 800w"
sizes = "(max-width: 320px) 280px,
(max-width: 480px) 440px,
800px"
src = "elva-fairy-800w.jpg" alt="Elva dressed as a fairy">
浏览器的查询过程:
- 查看设备宽度
- 检查sizes列表中哪个媒体条件是第一个为真
- 查看给予该媒体查询的槽大小
- 加载srcset列表中引用的最接近所选的槽大小的图像
参考文章: