经过几天的折腾,对移动端适配有了比较全面的了解,移动端适配需要解决的主要问题在于:
1、终端适配
2、retina高清屏幕带来的显示差异
本来想自己总结,但真的还差点火候,需要进一步的实践,所以转载一篇比较全面的文章,文章从物理像素、逻辑像素开始讲起、掠过viewport,直击移动端适配的痛点,最后,将retina屏幕的1px问题、以及适配方案归纳整理。附带源码,详细全面。因担心这么好的文章会消失掉,所以还是转到自己的博客比较好。
原文地址如下https://juejin.im/post/5bbb014ee51d450e452ae015,感谢博主‘梦想攻城狮‘的分享。
物理像素、逻辑像素
- 物理像素(设备像素):显示设备中一个最微小的物理部件。屏幕这块物理材料上横向和纵向共有多少个像素点,任何设备屏幕的物理像素出厂时就确定了,且固定不变
- 逻辑像素(设备独立像素):程序使用的虚拟像素(比如说CSS像素),程序认为横向和纵向有多少个像素点,然后由相关系统转换为物理像素
iPhone5 | iPhone6 | iPhone8Plus | |
---|---|---|---|
逻辑像素 | 568x320 | 667x375 | 736x414 |
物理像素 | 1136x640 | 1334x750 | 2208x1242 |
高清屏和像素比
像素点
图片是由很多的像素点构成,每个像素点显示的色彩不一样,构成了一副图片。你可以想象把蒙娜丽莎放大很多倍,然后裁成大小相同的小块,然后拼在一起,这个就是像素点
高清屏
同样大小的圆,里面容纳的正多边形的边数越多,其形状越接近圆,面积越接近圆。类似相同尺寸的屏幕,一个电容点所容纳的像素点越多,一个点可以由更多的像素点搭配来成像则效果当然更细腻,屏幕显得更清晰。
像素比
设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系,它决定了一个点是由多少个像素点来描绘。所以在开发的时候才会有@1、@2和@3图片区别。
iPhone5 | iPhone6 | iPhone8Plus | |
---|---|---|---|
逻辑像素 | 568x320 | 667x375 | 736x414 |
物理像素 | 1136x640 | 1334x750 | 2208x1242 |
屏幕倍率 | @2 | @2 | @3 |
dpr | 2 | 2 | 3 |
视口viewport
- 布局视口(layout):在html中一般在meta中的name为viewport字段就是控制的布局视口。布局视口一般都是浏览器厂商给的一个值,移动端在不设置的情况下为980px。
document.documentElement.clientWidth(clientHeight) // 布局视口的尺寸。
复制代码
- 视觉视口(visiual):浏览器可视区域的大小,即用户看到的网页的区域。(其宽度继承的layout宽度)
window.innerWidth(innerHeight) // 视觉视口尺寸
复制代码
- 理想视口(ideal):layout-viewport = width.screen.width(逻辑像素横向)。
<meta name="viewport" content="width=device-width">
复制代码
由于以前的PC页面尺寸比较小,手机在没有进行viewport设置时默认的时980px,可以兼容大多数情况,部分页面页面显示太小,部分页面显示不下而出现滚动条。所以为了达到理想视口,将layout-viewport设置和手机逻辑像素一样大小。但是由于高清屏,设计稿一般和手机物理像素一致,如果设置layout-viewport为逻辑像素,手机程序只会用逻辑尺寸长度去布局,那么横向只能显示正常设计稿的1/2或1/3,就会出现滚动条。
1px问题
在理想视口的情况下,layout-viewport的尺寸大小是逻辑尺寸大小,所以CSS中的1px就是逻辑尺寸的1px,在显示的时候,手机会根据dpr对其进行转化,导致CSS中的1px其实对应的两倍屏2px,三倍屏的3px,所以导致1px的线在这种情况下会变粗。
- IOS解决方案
.border { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
.border { border: 0.5px solid #999 }
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
.border { border: 0.333333px solid #999 }
}
复制代码
- :before, :after与transform(兼容)
.radius-border{
position: relative;
}
@media screen and (-webkit-min-device-pixel-ratio: 2){
.radius-border:before{
content: "";
pointer-events: none; /* 防止点击触发 */
box-sizing: border-box;
position: absolute;
width: 200%;
height: 200%;
left: 0;
top: 0;
border-radius: 8px;
border:1px solid #999;
-webkit-transform(scale(0.5));
-webkit-transform-origin: 0 0;
transform(scale(0.5));
transform-origin: 0 0;
}
}
复制代码
- viewport缩放
//dpr为2时,安卓下通过flexible.js动态设置无效,initial-scale=0.5始终默认为1
<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
复制代码
适配方案
从1px的适配中,可以知道viewport缩放是为了消除高清屏倍率的影响。所以一般适配主要从两方面着手:
- 利用viewport进行缩放来消除高清屏倍率的问题
- 利用rem来消除高清屏倍率的问题
- 利用rem调整不同设备之间的逻辑尺寸不一致的问题
固定viewport为理想视口
- 固定高度,宽度自适应:
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
复制代码
垂直方向使用固定的值,水平方向使用弹性布局,元素采用定值、百分比、flex布局等。这种方案相对简单,还原设计稿程度较低,尺寸一般都采用百分比和固定值,不能跟随设备变化而调整,高清屏的设计稿尺寸要缩小1/2或1/3。
- 根据不同屏幕动态写入font-size,以rem作为宽度单位。
var width = document.documentElement.clientWidth; // 屏幕的布局视口宽度
var rem = width / 7.5; //750px设计稿将布局视口分为7.5份
rem = px * 0.01; //1rem等于设计稿上的100px
在ipone6上:
width = document.documentElement.clientWidth = 375px;
rem = 375px / 7.5 = 50px;
0.75rem = 37.5px; (37.5/375=10%;占屏幕10%)
在任意其他机型上:
width = document.documentElement.clientWidth = 420px;
rem = (375px / 7.5)*(420/375) = 420/7.5 = 56px;
0.75rem = 42px = (420/375)*37.5px
复制代码
利用rem来消除高清屏倍率的问题,利用rem调整不同设备之间的逻辑尺寸不一致的问题,利用百分比还原设计图,但是无法消除1px的影响
动态设置viewport
- 根据不同屏幕动态写入font-size和viewport,以rem作为宽度单位。
var width = document.documentElement.clientWidth; // 屏幕的布局视口宽度
var rem = width / 10; // 将布局视口分为10份
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;
在ipone6上:
width = document.documentElement.clientWidth = 750px;
rem = 750px / 10 = 75px;
1rem = 75px;
在任意其他机型上:
width = document.documentElement.clientWidth = 840px;
rem = (840px / 10)*(420/375) = 420/7.5 = 94.08px;
1rem = 94.08px = (420/375)*75px
复制代码
利用viewport进行缩放来消除高清屏倍率的问题,能消除1px的问题,利用rem调整不同设备之间的逻辑尺寸不一致的问题,利用百分比还原设计图,但是其1rem等于75px或者其他不固定尺寸不方便计算。
- vw和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;
100vw = 100%width
复制代码
这种方式类似于rem = 750px / 10 的方案,只不过用vw来替代了rem,但是它有一个好处就是将rem解放出来,所以rem用来设置字体的大小,在移动端可以兼容用户设置字体大小的影响。
- 根据不同屏幕动态写入font-size和viewport,以rem作为宽度单位。为了消除这种不利的影响将上面进行修改:
var width = document.documentElement.clientWidth; // 屏幕的布局视口宽度
var rem = width / 7.5; //750px设计稿将布局视口分为7.5份
rem = px * 0.01; //1rem等于设计稿上的100px
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;
在ipone6上:
width = document.documentElement.clientWidth = 750px;
rem = 750px / 7.5 = 100px;
0.75rem = 75px;
在任意其他机型上:
width = document.documentElement.clientWidth = 840px;
rem = (750px / 7.5)*(840/750) = 840/7.5 = 56px;
0.75rem = 84px = (840/750)*75px
复制代码
利用viewport进行缩放来消除高清屏倍率的问题,能消除1px的问题,利用rem调整不同设备之间的逻辑尺寸不一致的问题,利用百分比还原设计图,方便计算。