移动端开发是最流行的,而对于移动端开发者适配是一个值得聊一聊的话题。适配,达到完成工作的目的其实很简单,但如果你想深入的了解其实还是有很多料的。会有很多大神跟你延伸出一堆的知识,我们只是来简单通俗的交流,来稍微深入一些了解相关知识。
开发移动端页面,大家第一步肯定是在head区引入这个标签, ,然后一顿操作,单位有的用px、有的用em、有的会用rem,然后再一点点挪动你从设计稿上量取的值,最后心满意足的发布上线,遇到显示有差异的手机再回来继续挪动的量取的值。
其实,这是一个正经的套路。不过有些问题如果在我们理解之后会更容易上手。我们可以稍微往深入的想想,这个标签是干什么的?
其实,这个meta视口标签只进行了几个简单的操作:设置页面的布局视口设定为理想视口、缩放比例为1、不允许进行缩放操作。注意,不要在页面重复的设置meta视口标签,对于有些安卓系统会出现你想都不敢想的问题。这个标签涉及到的属性:
width: 设置布局视口的宽
init-scale 设置页面的初始缩放程度
minimum-scale设置页面最小缩放程度
maximum-scale设置页面的最大缩放程度
user-scalable 是否允许用户进行缩放操作
我们先来放一个iphone手机的一些具体参数,下面有些不懂得可以进行对照就会清晰很多。
设备 屏幕尺寸 分辨率(pt) Reader 物理分辨率(px) PPI(DPI)
iphone4 3.5尺寸 320*480 @1x 320*480 163
iphone5 4尺寸 320*568 @2x 640*960 326
iphone6 4.7尺寸 375*667 @2x 750*1334 326
我们这里提到了理想视口、缩放等信息。很好理解,就是理想状态下的视觉窗口、缩大放小的比例规范吗?字面上是这么个意思。但是想要深入理解,我们需要理解几个简单的概念:
一、像素
这个简单,前端最常用的单位了,不过我们这里还是要简单说道说道:
像素本身是指计算机屏幕所能显示的特定颜色的最小区域,但前端开发的像素会包含设备像素和css像素。
设备像素,就是设备屏幕的物理像素,数量是固定的。
css像素,可以说是为web开发者创造的抽象概念。
如果你为一个元素设定了1000px的大小,那么这个元素是不是就跨越1000个设备像素呢?
并不一定是的。那么它会跨越多少个设备像素呢?这就要取决于手机屏幕的特性和用户的缩放了。
在早先的移动设备中,屏幕像素密度都比较低,一个css像素是等于一个设备像素的,当设置了width:1000px,1000个CSS像素跨越了1000个设备像素。但随着Retina屏的出现,分辨率提高一倍,而屏幕尺寸却没变,这时的css像素相当于两个物理像素。苹果手机还是比较规范,它的像素密度是普通屏幕的2倍。而安卓就五花八门了,安卓设备根据屏幕像素密度就可分为ldpi、mdpi、hdpi、xhdpi等等不同等级,分辨率更是多种多样。
除此之外,缩放也会引起css像素的变化,比如页面缩小一倍,css像素也会减少一倍。分辨率越大的移动设备,css的1px代表的物理像素就会越多。这是因为分辨率越大,而屏幕尺寸确没有变化,就会让css的1px代表更多的物理像素,devicePixelRatio的值也会越大。
二、视口
在移动端我们会涉及三个视口的概念。可以理解为视口是html元素的父元素,即为初始包含块。
视觉视口:
就是我们视觉看到的窗口大小,我们可以缩放的操作,而不会影响布局视口。
布局视口:
这个是我们最熟悉的了,移动端css布局的依据视口,即CSS布局会根据布局视口来计算。
移动设备上的viewport就是设备的屏幕上能用来显示我们的网页的那一块区域,但viewport为了兼容桌面浏览器的设计,又不局限于浏览器可视区域的大小,他可能比浏览器的可视区域要大,也可能要小。一般,移动设备的viewport为了兼容电脑显示都要大于浏览器的可视区域的,这就会出现滚动条的显示。不过它也有好处的,就是拿掉了移动端布局对于屏幕宽度的限制。
我们可以通过JS来获取布局视口的宽度和高度(不过存在一丢丢的兼容性,正常情况下都是没问题的):
document.documentElement.clientWidth
document.documentElement.clientHeight
理想窗口:
这个可以说是为了我们的适配专门引入的,就是理想状态下的视口宽度。在移动设备中就是指设备的分辨率。换句话说,理想视口或者说分辨率就是给定设备物理像素的情况下,最佳的“布局视口”。比如iphone5理想视口就是320*568。但对于安卓手机理想视口的默认值都很不理想,我们需要meta视口标签进行手动设定。上面说到CSS是根据布局视口来进行计算的,我们将布局视口设为理想口来方便我们进行适配,就是引入的meta视口标签:。而能达到同样的效果。为什么呢?因为缩放就是基于理想视口进行的,当前缩放值 = 理想视口宽度 / 视觉视口宽度。对于同时出现这两种设定,会取宽度结果的最大值。比如你设定width=600px,而按照initial-scale进行缩放之后的值是420px,那么就会取420px。另外,initial-scale的值是越大,缩放得到的结果越小。
三、设备像素比(DPR)
1、Device Pixel Ratio
window对象的devicePixelRatio属性,官方定位为,设备物理像素和设备独立像素的比例,但是有些浏览器对这个属性是有兼容问题的。DPR=物理像素/分辨率,可以理解为设备像素比DPR = 设备像素个数/理想视口CSS像素个数(device-width)
// 注:前提是缩放比是1,在不缩放的情况下,一个css像素就对应一个dpr。
设备像素比对于不同的设备是不同的,比如早先的iphone的设备像素是320px,理想视口也是320px,
所以早先的iphone的DPR是1,而后来的iphone的设备像素是640px,理想视口还是320px,
所以后来的iphone的DPR是2.
现在主流手机显示屏是2 or 3,高密度桌面显示屏是2.
简单介绍一下倍图
有一张图片,你设置了宽高都是50px。
你在普通屏幕下打开是完全没问题的,但是如果在高清屏下打开,按照逻辑分辨率渲染(这时的devicePixelRatio=2)就会变得模糊,这时你会需要使用@2x甚至@3x来避免图片失真。这个@2x和@3x就是所谓的几倍图(这里需要用到CSS3的image-set的CSS属性或是html5的srcset)。
2、缩放
缩小或放大的CSS像素
缩放就是基于理想视口进行的,当前缩放值 = 理想视口宽度 / 视觉视口宽度。
3、meta标签
meta视口标签
width: 设置布局视口的宽
init-scale 设置页面的初始缩放程度
minimum-scale设置页面最小缩放程度
maximum-scale设置页面的最大缩放程度
user-scalable 是否允许用户进行缩放操作
四、js动态设置viewport的方案
目前最好的解决方案。
var scale = 1 / window.devicePixelRatio;
document.querySelector('meta[name="viewport"]').setAttribute('content','width=device-width,initial-scale=' +scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
作用是直接将理想视口设定与设备像素的相同,直接按设计稿的尺寸不需要进行换算,然后在其他尺寸的手机中,
我们进行等比缩放就ok了。
我们知道,一般我们获取到的视觉稿大部分是iphone6的,所以我们看到的尺寸一般是双倍大小的,
在使用rem之前我们一般自觉的将标注/2,其实完全可以按照视觉稿的尺寸来设置。
1).设计给的稿子双倍的原因是iphone6这种屏幕属于高清屏,即设备的像素比dpr比较大,
所以显示的像素较为清晰。
2).一般手机的dpr是1,iphone4和5是2,iphone6s plus这种是3,可以通过window.devicePixelRatio来获取当前设备的dpr,所以iphone6的视觉稿大小是(*2)750*1334了。
3).拿到了dpr之后,我们就可以在viewport meta头里,取消让浏览器自动缩放页面,
而自己去设置viewport的content例如(这里之所以要设置viewport是因为我们要实现border1px的效果,在scale的影响下,高清屏会显示成0.5px的效果)
五、实现方式
1、百分比
其实在高度上并没有很好的自适应。会出现一些比较奇怪的问题。
还有一个比较明显的问题就是:
在任何机型上我们的按钮的高度是不会变化自适应的,宽发生变化但是高度没有变化,所以小屏手机我们的按钮看起来很臃肿
2、媒体查询
响应式设计的基础:
检测媒体的类型,比如screen,tv等
检测布局视口的特性,比如视口的宽到分辨率等
特性相关查询,比如检测浏览器是否支持某某特性(无用)
语法:
@media 媒体类型 and (视口特性阈值) {
// 满足条件的样式
}
eg.
@media all and (min-width: 321px) and (max-width: 400px) {
.bx {
xxx
}
}
缺点:
相对于代码要重复很多
可能存在闪屏的问题出现
常用的临界点:
1199px
991px
767px
480px
375px
320px
五、一些常用单位的概念简析
px
相对长度单位。是相对于显示器屏幕分辨率而言的(也就是跟物理设备有关)。
高清屏和普通屏对比就是普通屏的1个像素就是1个物理像素,而高清屏的是4个
物理像素点。
em
相对长度单位。相对于当前对象内文本的字体尺寸。
如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸。
em单位特点:
1.em的值并不是固定的
2.em会继承父级元素的字体大小
rem
相对长度单位。CSS3新增的一个相对单位,这个单位引起了广泛的关注。与em的
区别在于rem为元素设定字体大小时,仍是相对大小,但相对的只是HTML根元素。
这个单位可谓集相对大小和绝对大小的优点于一身,通过它既可以做到只修改根
元素就成比例地调整所有字体大小,又可以避免字体大小逐层复合的连锁反应。
缺点
IE不支持,PC端使用次数不多
数据量大,所有的图片、盒子都需要精准的值完成适配
弱网条件下会出现可能会出现闪屏:
(解决闪屏就是要在CSS渲染的时候,就让HTML显示正常,在开始的时候就进行font-size的计算,
@media (min-width: 320px){html{font-size: 42.6667px;} }
@media (min-width: 360px){html{font-size: 48px;} }
@media (min-width: 375px){html{font-size: 50px;} }
@media (min-width: 384px){html{font-size: 51.2px;} }
@media (min-width: 414px){html{font-size: 55.2px;} }
@media (min-width: 448px){html{font-size: 59.7333px;} }
@media (min-width: 480px){html{font-size: 48px;} }
@media (min-width: 512px){html{font-size: 68.2667px;} }
@media (min-width: 544px){html{font-size: 72.5333px;} }
@media (min-width: 576px){html{font-size: 76.8px;} }
@media (min-width: 608px){html{font-size: 81.0667px;} }
@media (min-width: 640px){html{font-size: 85.3333px;} }
@media (min-width: 750px){html{font-size: 100px;} })
注:
任意浏览器的默认字体大小都是16PX。所有未经调整的浏览器都符合:1rem=16px.
那么12px=0.75rem;为了简化font-size的换算,需要在css中的html选择器中声明
font-size=62.5%,这就是使rem值变为16px*62.5%=10px,这样12px=1.2rem,也
就是说只需要将原来的px数值除以10,然后换上rem作为单位就行。
在使用rem的时候比较麻烦的就是px和rem换算的问题。上面的除10方案是比较简单
的。但是根据基准值的不同换算方法也不一样。如果我们使用scss来写我们的样式
表的话,解决方法就比较简单了,代码如下:
@function px2rem($px){
@return ($px/10)/2+rem; //相当于$px/20 +rem
}
width:px2rem(100px); //5rem
height:px2rem(200px); //10rem
为什么使用rem作单位
对于设计稿中展示的商品的坑位的宽高比是固定的。为了在不同屏幕上达到最佳的
显示效果,需要保持宽度和高度一致。而百分比对于高度是没有办法控制的。目前
最优的解决方案就是rem。
为什么将计算根元素的font-size值的js放在head标签中
设置根节点font-size值的方法
一是使用css的Media queries
eg:
@media(min-device-width:375px)and(max-device-width:667px)and(-webkit-min-device-pixel-ratio:2){
html{font-size:37.5px;}
}
这种方法的一个很明显的问题font-size是在一个屏幕宽度的区间上有一个基准值。对于屏幕大小很多,该方法很鸡肋。
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';
将rem设定为body屏幕宽度的十分之一。
二是JS根据当前屏幕的宽度动态计算font-size值
可以保证屏幕宽度连续变化时,font-size基准值也是连续变化的。
注:为了保证不发生不必要的重绘或者是重排,所以放在head区。
使用rem布局的情况和注意问题
1.整体的布局还是使用百分比。
2.使用rem的最佳场所是,遇到例如多列带有图片的列表,常常需要图片固定宽高比
3.对字体一般使用px(防止出现13.7172727px这种在)
4.出现1px的地方仍旧使用px
点5像素的由来
高清屏可以识别0.5像素的效果。
实现点5像素的方法
伪元素+css3的缩放巧妙实现
1.设置目标元素定位参照
2.给目标元素添加一个为元素before或者after,并设置绝对定位
3.给伪元素添加上1px的边框
4.设置伪元素的宽高为目标元素的两倍
5.缩小0.5倍
6.使用border-box把border包进来
.item4, .item5 {
width: 200px;
height: 100px;
position: relative;
}
.item4 {
border: 1px solid #000;
}
.item5::before {
content: '';
position: absolute;
width: 200%;
height: 200%;
border: 1px solid #000;
transform-origin: 0 0;
transform:scale(0.5, 0.5);
box-sizing: border-box;
}
六、实现方法和具体步骤
第一种修改meta标签
1、将布局视口大小设为设备像素尺寸:
var scale = 1 / window.devicePixelRatio;
document.querySelector('meta[name="viewport"]').setAttribute('content','width=device-width,initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
2、动态设置html字体大小:
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';
3、将设计图中的尺寸换算成rem
元素的rem尺寸 = 元素的psd稿测量的像素尺寸 / 动态设置的html标签的font-size值
body{
margin: 0;
padding: 0;
}
.box{
width: 2.66666667rem;
height: 2.66666667rem;
background: red;
}
var scale = 1 / window.devicePixelRatio;
document.querySelector('meta[name="viewport"]').setAttribute('content','width=device-width,initial-scale=' +scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';
//---------------------------------------
测试
(function() {
// deicePixelRatio :设备像素
var scale = 1 / devicePixelRatio;
//设置meta 压缩界面 模拟设备的高分辨率
document.querySelector('meta[name="viewport"]').setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
//debounce 为节流函数,自己实现。或者引入underscoure即可。
var reSize = _.debounce(function() {
var deviceWidth = document.documentElement.clientWidth > 1300 ? 1300 : document.documentElement.clientWidth;
//按照640像素下字体为100px的标准来,得到一个字体缩放比例值 6.4
document.documentElement.style.fontSize = (deviceWidth / 6.4) + 'px';
}, 50);
window.onresize = reSize;
})();
html {
height: 100%;
width: 100%;
overflow: hidden;
font-size: 16px;
}
div {
height: 0.5rem;
widows: 0.5rem;
border: 0.01rem solid #19a39e;
}
........
第二种不修改meta标签,按照1:1的比例进行:
1、拿到设计图,计算出页面的总宽,为了好计算,取100px的font-size,如果设计图是iPhone6的那么计算出的就是7.5rem,如果页面是iPhone5的那么计算出的结果就是6.4rem。
2、动态设置html标签的font-size值:
document.documentElement.style.fontSize = document.documentElement.clientWidth / 以rem为单位的页面总宽 + 'px';
如iPhone6的设计图就是:
document.documentElement.style.fontSize = document.documentElement.clientWidth / 7.5 + 'px';
iPhone5的设计图就是:
document.documentElement.style.fontSize = document.documentElement.clientWidth / 6.4 + 'px';
这样,无论在什么设备下,我们页面的总宽度都是7.5rem,所以我们直接在设计图上测量px单位的尺寸,然后除以100转换成rem单位后直接使用就可以了
3、做页面是测量设计图的px尺寸除以100得到rem尺寸。
4、和淘宝的做法一样,文字字体大小不要使用rem换算。