概念

对于移动端开发来说,无可避免的就是直面各种设备不同分辨率和不同DPR(设备像素比)的问题,在此忽略其他兼容性问题的探讨。

移动端像素

  1. 设备像素(dp),也叫物理像素。指设备能控制显示的最小物理单位,意指显示器上一个个的点。从屏幕在工厂生产出的那天起,它上面设备像素点就固定不变了。

  2. 分辨率,屏幕上物理像素的数量。

  3. 设备独立像素(dip),又称密度无关像素。可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用并控制的虚拟像素。由相关系统转化为物理像素在设备上体现。

  4. css像素,web编程中的概念,属于设备独立像素中的一种,独立于设备,属于逻辑上衡量像素的单位。

  5. 设备像素比(dpr) = 设备像素值(dps) / 设备独立像素值(dips),代表系统转化时一个css像素占有多少个物理像素。

  6. 像素密度(ppi),设备(屏幕)每英寸内有多少个像素点。

移动端三个视口

移动端视口 viewport(div100%时的css大小):移动设备上的 viewport 就是设备的屏幕上能用来显示我们的网页的那一块区域,可能与浏览器的可视区域不同。默认比浏览器可视区域要大(980px),这也是为什么一般的PC端网页放在移动端会出现横向滚动条的原因。

移动端中的三个不同的可视区域大小,来自于ppk关于移动设备的viewport研究:

  1. 布局视口(layout viewport),浏览器默认的viewport,一般比浏览器可视区域大。

  2. 视觉视口(visual viewport),浏览器的可视区域大小(浏览器的可见区域css像素值)

  3. 理想视口(ideal viewport),设备的实际物理宽度(device-width),是一种与ppi无关的设备原始的宽度(英寸),例如320px和660px下的iphone的理想视口都是320px。

位图像素

一个位图像素是栅格图像(如:png, jpg, gif等)最小的数据单元。每一个位图像素都包含着一些自身的显示信息(如:显示位置,颜色值,透明度等)。

理论上,1个位图像素对应于1个物理像素,图片才能得到完美清晰的展示。当遇上对应的位图像素与物理像素不统一的时候。

  1. 位图像素 < 物理像素。 1个位图像素对应于多个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊。(具体取决于设备系统的图像算法,并不是简单的切割图片)(图片拉伸)

  2. 位图像素 > 物理像素。1个物理像素对应多个位图像素,所以它的取色也只能通过一定的算法(显示结果就是一张位图像素只有原图像素总数四分之一的图片),肉眼看上去虽然图片不会模糊,但是会觉得图片缺少一些锐利度,或者是有点色差(但还是可以接受的)(图片挤压)

rem适配

什么是rem

即以根节点(html)的字体大小作为基准值进行长度计算。

假定 html 的 fontSize 为 16px,则 1rem = 16px

如果我们更改 html 的 fontSize,rem 也会更新,总是保持 1rem = 1 fontSize (html)

为什么使用rem

开发过移动端项目的同学应该都知道,不同手机设备的大小是不一样的,在进行移动端开发时,我们通常会为 html 加上 viewport meta

<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

这里得结合上面的移动端像素和移动端视口进行分析,width=device-width将此时的页面宽度设置为设备宽度(理想视口),所以此时页面宽度等于设备宽度,不同手机的设备宽度是不同的所以页面宽度也不同

iPhone4 页面宽度 = window.innerWidth = 设备宽度 = 320px

iPhone6 页面宽度 = window.innerWidth = 设备宽度 = 375px

所以为了适配不同的设备宽度,我们通常不直接用px来写css代码,因为在不同手机中页面宽度不同,此时px的相对大小也是不同的。如果我们把一个元素设置为375px来达到100%宽度效果的话,在320设备宽度的手机就出问题了。

由此我们引入了 rem 来做适配,在 css 中直接使用 rem 作为计量单位,如果不做些什么的话,1rem = 16px(浏览器默认字体大小),在不同手机上都是一样,还是无法适配,所以要点在于如何根据设备宽度在做转化

// 假定设计稿宽度750px
const designWidth = 750; // 通过设备宽度(window.innerWidth)和设计稿宽度(designWidth)的比例来设置 html fontSize
document.documentElement.style.fontSize = (window.innerWidth / designWidth) + 'px';

通过上面代码的设置,我们就可以很轻松的适配移动端项目了,假定设计稿上一个元素宽度750px,那我们就在css定义750rem

在设备宽度为320px的手机上

750rem = 750 * 1rem = 750 * (window.innerWidth / designWidth) px = 750 * (320 / 750) px = 320px

同理,在设备宽度为375px的手机上

750rem = 750 * 1rem = 750 * (window.innerWidth / designWidth) px = 750 * (375 / 750) px = 375px

可能还有个问题,为什么不直接用百分比来适配?因为百分比在很多情况下是除不尽或者带有小数的,显然带有小数点的px会带来各种各样的误差

高清适配

如果你觉得移动端适配像上面一样简单转化下就行,那就 too young too sample

1px问题

什么是 1px 问题?

以 iphone6 为例,大家应该听过啥视网膜像素之类的,2倍屏之类的吧。其实也就是此时 设备像素比(dpr) = 设备像素值(dps) / 设备独立像素值(dips),即一个css像素对应两个物理像素,也就是你在css中写的1px其实在设备显示的是两个像素,当你设置 border = 1px 时看起来就没有那种1px的纤细效果,总感觉不尽如人意,差那么一点点味道。

你以为的1px

用户看到的1px(请忽略颜色不同)

追求用户体验的公司通常是不能容忍 1px 问题的

图片的模糊问题

同样的以 iphone6 为例,我们如果定义一张图片宽度为375px,如果图片的像素(位图像素),此时一个像素的图片会对应两个物理像素(参考上面的位图像素),就会造成图片模糊的问题了。你可能会问?那我直接加载750px像素的图片不就好了(位图像素大于物理像素时很多人是看不出失真的)。

答案当然是可以的,但你觉得追求用户体验的公司能容忍无故的流量耗费和性能浪费么?当然不能

解决方案

<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

前面也有介绍过这部分代码,但是没有说明 initial-scale=1 的作用,initial-scale 定义了页面的初始缩放,1代表不缩放。initial-scale的值也会影响页面宽度,即此时的css像素。

前面我们说过,在 viewport meta 的约束下

页面宽度 = window.innerWidth = 设备宽度,但其实正确的是 页面宽度 = window.innerWidth = 设备宽度 / scale,为什么是除呢?大家可以想象一下,当页面缩放时(例如scale=0.5),是不是会导致更多的内容内容展示在当前可见区域中,css像素(页面)是变大了。

以 iphone6 为例,当我们设置

<meta name="viewport" content="width=device-width, initial-scale=0.5, user-scalable=no">

此时页面宽度 = window.innerWidth = 设备宽度 / scale = 375 / 0.5 = 750px,也就是说现在页面宽度(对应css像素)和物理像素是相等的,所以我们设置的 1px 在手机中将真正显示 1pt(1个物理像素),也就解决了1px的问题。

所以解决方法如下

// 获取设备dpr
const dpr = window.devicePixelRatio; // 计算缩放比例
const scale = 1 / dpr; // 动态设置meta
const metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'width=device-width,initial-scale=' + scale + ', user-scalable=no');

对应图片而言,要想达到最清晰的显示状态则要使图片的位图像素与设备的物理像素对应,所以可以对图片做如下适配

[dpr=1] img {

width: 200rem;

background: '@1x.png';

}

[dpr=2] img {

width: 200rem;

background: '@2x.png';

}

此方案的原理就是利用meta来更过css像素(因为css像素是虚拟像素由计算机定义的,见上文),以此达到一个css像素对应一个物理像素的效果,1px == 1pt

rem高清适配

利用上文提供的rem移动端适配思路,加上现在的高清适配思路,就可以完成移动端高清适配啦

直接贴代码,来自前端:『REM』手机屏幕高清适配方案

(function(designWidth, rem2px) {
var win = window;
var doc = win.document;
var docEl = doc.documentElement;
var metaEl = doc.querySelector('meta[name="viewport"]');
var dpr = 0;
var scale = 0;
var tid; if (!dpr && !scale) {
var devicePixelRatio = win.devicePixelRatio;
if (win.navigator.appVersion.match(/iphone/gi)) {
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
dpr = 1;
}
scale = 1 / dpr;
} docEl.setAttribute('data-dpr', dpr);
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'width=device-width,initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no'); if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
} else {
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'width=device-width,initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
}
// 以上代码是对 dpr 和 viewport 的处理,代码来自 lib-flexible。 // 一下代码是处理 rem,来自上篇文章。不同的是获取屏幕宽度使用的是
// document.documentElement.getBoundingClientRect
// 也是来自 lib-flexible ,tb的技术还是很强啊。
function refreshRem(_designWidth, _rem2px){
// 修改viewport后,对网页宽度的影响,会立刻反应到
// document.documentElement.getBoundingClientRect().width
// 而这个改变反应到 window.innerWidth ,需要等较长的时间
// 相应的对高度的反应,
// document.documentElement.getBoundingClientRect().height
// 要稍微慢点,没有准确的数据,应该会受到机器的影响。
var width = docEl.getBoundingClientRect().width;
var d = window.document.createElement('div');
d.style.width = '1rem';
d.style.display = "none";
docEl.firstElementChild.appendChild(d);
var defaultFontSize = parseFloat(window.getComputedStyle(d, null).getPropertyValue('width'));
// d.remove();
var portrait = "@media screen and (width: "+ width +"px) {html{font-size:"+ ((width/(_designWidth/_rem2px)/defaultFontSize)*100) +"%;}}";
var dpStyleEl = doc.getElementById('dpAdapt');
if(!dpStyleEl) {
dpStyleEl = document.createElement('style');
dpStyleEl.id = 'dpAdapt';
dpStyleEl.innerHTML = portrait;
docEl.firstElementChild.appendChild(dpStyleEl);
} else {
dpStyleEl.innerHTML = portrait;
}
// 由于 height 的响应速度比较慢,所以在加个延时处理横屏的情况。
setTimeout(function(){
var height = docEl.getBoundingClientRect().height;
var landscape = "@media screen and (width: "+ height +"px) {html{font-size:"+ ((height/(_designWidth/_rem2px)/defaultFontSize)*100) +"%;}}"
var dlStyleEl = doc.getElementById('dlAdapt');
if(!dlStyleEl) {
dlStyleEl = document.createElement('style');
dlStyleEl.id = 'dlAdapt'
dlStyleEl.innerHTML = landscape;
docEl.firstElementChild.appendChild(dlStyleEl);
} else {
dlStyleEl.innerHTML = landscape;
}
},500);
} // 延时,让浏览器处理完viewport造成的影响,然后再计算root font-size。
setTimeout(function(){
refreshRem(designWidth, rem2px);
}, 1); })(750, 100);

代码比较多,有兴趣的可以直接上github上找到源代码(https://github.com/hbxeagle/rem/blob/master/HD_ADAPTER.md)

后记

这是一篇很早之前写的总结了,今天又复习修改了一下,写的有错误或者写的不清楚的地方请大家多多指正。

这么多年过去,其实现在已经逐渐流行直接使用 vw vh 来做移动端适配了,因为随着设备的更新兼容性的问题已经大大减少。但使用 rem 模式还是有一定需求的,毕竟vw还没有全部兼容,可以参考vw兼容性。还有就是有pc浏览器打开并限制最大宽度的需求使用vw就不可以了。

后面有时间将写写利用 vw vh 来进行移动端适配的总结,会比这个简单。

参考


欢迎到前端学习打卡群一起学习~516913974

移动端适配-rem(新)的更多相关文章

  1. 移动端适配 rem

    前置知识: 物理像素(physical pixel,device pixel) 物理像素(设备像素),显示设备中一个最微小的物理部件.每个像素可以根据操作系统设置自己的颜色和亮度. 设备独立像素(de ...

  2. 浅谈移动端适配-rem

    对于移动端开发来说,无可避免的就是直面各种设备不同分辨率和不同DPR(设备像素比)的问题,在此忽略其他兼容性问题的探讨. 一. 移动端开发有关于像素的概念: 1.设备像素(dp),也叫物理像素.指设备 ...

  3. 移动端适配rem为单位的rem.js及个别设备设置了大字体模式,导致页面变形的处理方式

    这段时间内,涉及到的都是移动端开发,说到移动端开发,我们就会思考到,目前分辨率的问题,如果用px为单位的话,在不同移动设备和不同分辨率下,页面的效果可能会有所不同,甚至导致页面变形.所以在次我们就用到 ...

  4. 移动端适配 rem 设置

    refresh();    window.onresize = function(){      setTimeout(function(){        refresh();      },10) ...

  5. 移动端适配 后篇(rem+vm)

    涉及到的一些名词, 详细解释可参考 移动端适配前篇--移动端适配 rem 名词解释 [英寸Inch]英寸表示屏幕斜对角线的长度 [像素Pixel]像素是图像的基本采样单位,它不是一个确定的物理量,因为 ...

  6. css样式重置 移动端适配

    css  默认样式重置 @charset "utf-8"; *{margin:0;padding:0;} img {border:none; display:block;} em, ...

  7. 移动端使用rem适配及相关问题

    移动端适配方案,说多也很多.可以使用百分比布局,但百分比与em都是基于父元素进行计算的,在实际应用中不是很方便.使用rem不仅可以设置字体大小,块大小也可以设置.而且可以良好的适配各种终端,所以这方案 ...

  8. 移动端页面开发适配 rem布局原理

    主题 HTML移动端页面开发适配 rem布局原理 什么是适配,为什么要适配 我们拿到的设计图一般是以640,750,1080分辨率为基准设计的,而现在的手机终端各式各样,分辨率不同,逻辑像素不同 ,适 ...

  9. 小tips:使用rem+vw实现简单的移动端适配

    首先设置meta属性,如下代码: <meta name="viewport" content="width=device-width, initial-scale= ...

随机推荐

  1. 用Redislive监控redis

    注意:RedisLive是使用Python2.x编写,建议使用2.7,本次环境为Centos 7.2,默认Python版本2.7. 项目地址:https://github.com/nkrode/Red ...

  2. rabbitmq启动时出错epmd error for host

    centos7环境下新装rabbitmq,第一次启动时发现出错:ERROR: epmd error for host "****":XXXXXXX 检查发现当前机器的名称为 1  ...

  3. kafka可插拔增强如何实现?

    导弹拦截,精准防御. 背景 拦截器:在不修改应用程序业务逻辑的情况下,一组基于事件的可插拔的逻辑处理链: 类比springMVC的拦截器: 这些都是通过配置拦截器,插入到应用程序中,实现可插拔的修改业 ...

  4. Flutter 粘合剂CustomScrollView控件

    老孟导读:快乐的51假期结束了,切换为努力模式,今天给大家分享CustomScrollView组件,此组件在以后的项目中会经常用到,CustomScrollView就像一个粘合剂,将多个组件粘合在一起 ...

  5. Java——运算符那些事

    && 逻辑与 &&先运算&&左边的算式,如果为假,则直接停止,后面不管有多少运算式都不再运算,如果为真则继续判断后面的式子,只有所有的条件全部成立,才会 ...

  6. Spring官网阅读(十)Spring中Bean的生命周期(下)

    文章目录 生命周期概念补充 实例化 createBean流程分析 doCreateBean流程分析 第一步:factoryBeanInstanceCache什么时候不为空? 第二步:创建对象(crea ...

  7. 【Hadoop离线基础总结】zookeeper的介绍以及集群环境搭建、网络编程和RPC的简单了解

    ZooKeeper的介绍以及集群环境搭建.网络编程和RPC的简单了解 ZooKeeper介绍 概述 ZooKeeper是一个分布式协调服务的开源框架,主要用来解决分布式集群中应用系统的一致性问题.例如 ...

  8. webpack3 项目升级 webpack4

    由于 vue-cli 2 构建的项目是基于 webpack3,所以只能自己动手改动,至于升级 webpack4之后提升的编译速度以及各种插件自己去体验. 修改配置 1.替换插件 extract-tex ...

  9. linux-rpm强制安装跳过依赖包

    [root@localhost ~]# rpm -ivh tigervnc-1.10.80-4.20200317git8b4be5fd.el7.x86_64.rpm --nodeps --force ...

  10. java ->EL技术&JSTL技术

    EL技术 EL 表达式概述 EL(Express Lanuage)表达式可以嵌入在jsp页面内部,减少jsp脚本的编写,EL出现的目的是要替代jsp页面中脚本(java代码)的编写. EL从域中取出数 ...