真的,移动端尺寸自适应与dpr无关
做移动端自适应时可能很多人都对自适应和dpr之间的关系产生疑问,也有一些人会疑虑比如我的自适应方案没有加dpr会不会出问题,针对这些疑问我说一下我的见解。
1. 什么是尺寸自适应
首先标题说的自适应,可能自适应在不同人眼里理解不同,特别与响应式的关系,在这里说一下我所理解的自适应,和其与响应式的区别。先说响应式设计,响应式设计表示在不同的屏幕尺寸下,都有良好的布局和内容表现,简单一点的说,就是一个页面可以适配多种不同尺寸的屏幕,而且看上去还是设计良好的。为了实现这个目的,可能会利用js或者css去动态改变布局的尺寸,在这个过程中会伴随元素尺寸的改变,布局的改变,甚至会把元素隐藏,比如在pc端显示的页面转到移动端就会这样。而自适应往往考虑的是另一个方面,就是希望页面的设计与设计稿的设计比例一致,这个也是做自适应的目的,在这个过程中针对不同的屏幕宽度元素的尺寸也会改变,但是一般不会有布局改变,和元素的隐藏,因为设计稿就这样,我们得按设计师妹子的尺寸来写页面。所以按照我以上的说法,那些按照css媒体查询写的自适应严格来说不叫自适应,因为断点之间会造成比例误差,而让误差少一点就得多插值。很明显使用css媒体查询并不是做自适应的好方法,我们需要一种准确的方法来做这个事,这个时候js就出来,下面将会列举坊间流传甚广的淘宝方案和网易方案。
2. 淘宝方案
点这里可以看到淘宝方案具体的代码[flexible][1]
当然具体的代码是做了很多的边界处理和兼容处理的,但是核心可以浓缩为以下代码
(function () {
var dpr = window.devicePixelRatio;
var meta = document.createElement('meta');
var scale = 1 / dpr;
meta.setAttribute('name', 'viewport');
meta.setAttribute('content', 'width=device-width, user-scalable=no, initial-scale=' + scale +
', maximum-scale=' + scale + ', minimum-scale=' + scale);
document.getElementsByTagName('head')[0].appendChild(meta);
// 动态设置的缩放大小会影响布局视口的尺寸
function resize() {
var deviceWidth = document.documentElement.clientWidth;
document.documentElement.style.fontSize = (deviceWidth / 10) +'px';
}
resize();
window.onresize = resize;
})()
这段代码放在浏览器上就能做到自适应了,他的过程是先获取设备的dpr,所谓的dpr就是设备像素比,什么是设备像素比呢,就是单位尺寸内,设备物理像素的个数除以设备独立像素的大小,物理像素就是手机屏幕上一个一个的发光的点,大小是固定的,独立像素也叫做逻辑像素,css设置的像素大小就是逻辑像素,对于dpr等于2的手机屏幕,设置css宽度为1px,其实覆盖的是2个设备物理像素。回到正题,拿到dpr后,通过动态设置meta的viewport值,进行对布局的缩放操作。这里有一个关键,就是设置 width=device-width和initial-scale的大小,在描述两者的作用之前我们先要理解一个概念就是布局视口,布局视口在之前有一个别名叫做初始包含块,而在比较早的文献中初始包含块也叫做画布。理解画布可能比理解布局视口更简单,如果你按比例绘图,很多时候就要参照你所用画布的大小,比如设计师在750px画了一个200px的正方形,如果你要在一张大小是100cm的纸上画,你可能就要这样计算正方形的宽度了 100cm * 200 / 750,可以看到这个计算中是没有用到dpr,你的笔触跨过多少个纸张分子,多少个原子根本就不影响我的绘图比例。我们的画画的过程就相当于设置css的过程,css的尺寸依赖的就是布局视口的大小,而网页的布局视口大小在标准模式下可以这样获取 document.documentElement.clientWidth
,而两个关键的元素设置 width=device-width
,initial-scale = scale
,做的事情就是先把布局视口放大dpr倍,然后整体缩放相应倍数以适应设备尺寸。这个也很容易验证在控制台打印布局视口大小就行了
这是按照640px设计规范,设计图上标注200px元素大小,可以看出布局视口放大了3倍,然后再整体缩放到设备屏幕大小,由于这里是证明这个过程其实与dpr无关,我现在把scale的大小设置为
0.1 和 0.5
var meta = document.createElement('meta');
var scale = 0.1;
meta.setAttribute('name', 'viewport');
var meta = document.createElement('meta');
var scale = 0.5;
meta.setAttribute('name', 'viewport');
这里可以看到就算我设置scale不等于 1 / drp 的大小也不妨碍我按设计图的比例画出元素
这里要注意两点,因为我是用chrome模拟的,设置的时候发现几个问题
- scale的值如果小于0.1布局视口也只能放大10倍,也就是布局视口最多放大10倍
- 当scale的值大于1时布局视口并不会缩小,而且布局视口不再匹配设备宽度
- 如果你引入了flexible.js进行测试,要注意删除边界条件,因为缩放影响了布局视口大小,相应的边界条件会触发,导致误认为dpr与自适应有关
要做到自适应关键是让元素的尺寸与布局视口绑定关系,在这里虽然布局视口放大了,但并不影响这种绑定关系,这里淘宝方案把布局视口的宽度分割了十等份,每份的大小相当于布局宽度的十分之一,而把每份的大小分配给根元素的字体大小,元素尺寸就可以设置rem单位来与布局视口绑定关系,以200px尺寸为例,他们比例映射是这样的
200px : 640px => xrem : 10rem
这里的10rem就是布局视口宽度,元素尺寸只要维持这个比例关系就行了,与dpr是没有关系的
x= 10 * 200 / 640 = 3.125rem
这里的计算可能会费一点时间,也有一些插件可以辅助把px转为rem的
但是方案是死的,人是活的,你只要把淘宝固有的十等分改一下就行了,比如设计稿是640px的
改一下
document.documentElement.style.fontSize = (deviceWidth / 6.4) +'px';
分了6.4等份
200px : 640px => xrem : 6.4rem
x一看就知道是 2rem
流程 rem => 根元素字体大小 => 布局视口
那么为什么淘宝要引入dpr,把布局放大再缩小呢,其中一点就是这个方案可以很好地解决1px边框的问题,对于高清屏来说设置1px像素大小,其实横跨的是dpr个设备像素,这样看起来线条不够细,与设计稿就产生出入,而通过布局放大再缩小的方案刚好就弥补了这个问题。但是随之而来也带来一个问题,看上面的截图我们看到字体大小发生了改变,在scale设置为0.1时基本就看不见了,原因是一般我们的字体大小的设置不会使用rem,而是使用px单位,这里的字体大小没有随布局视口的放大而增大,却随页面的整体缩放而缩小了,这里就得要针对不同的dpr做响应的处理,在淘宝的代码中我们可以看到
docEl.setAttribute('data-dpr', dpr);
就是通过在根元素上挂载dpr信息,然后设置相应的css属性例如
[data-dpr=2] div{
font-size: 32px
}
[data-dpr=3] div{
font-size: 48px
}
特别对于安卓手机,各种神奇的dpr,如果每个都这样设置将会是灾难
所以淘宝非常聪明
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
够简单直接,安卓高清屏是不存在的, 但是其实影响也不大,就是安卓屏的1px线条粗一点而已
如果除了要做自适应还要做响应式,那也得像上面设置字体一样一个一个设置,因为css媒体查询也是针对布局视口尺寸的。对于淘宝他们来说,肯定有一套工程化的方案来解决这种技术难题,对于遇到这个坑的伙伴估计得自已想办法了,预处理器是必不可少的。
从前面可以知道淘宝引入dpr并不是为了做自适应的,而是为了解决1px问题的,当然也引入了其他难题,既然如此,放弃解决1px问题,不就简单得多,网易方案就是这么简单。
3.网易方案
去除了边界处理和兼容处理,由于没有动态设置meta所以要在head中引入
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
(function () {
var dpr = window.devicePixelRatio;
function resize() {
var deviceWidth = document.documentElement.clientWidth;
document.documentElement.style.fontSize = (deviceWidth / 6.4) +'px';
}
resize();
window.onresize = resize;
})()
网易方案没有引入dpr相关的,这也说明了移动端自适应与dpr是无关的
从图片中可以看出和淘宝方案的区别,布局视口没有放大,整个页面也没有缩放,但是并不影响与设计图的比例
200px : 640px => xrem : 6.4rem
x= 2rem
流程 rem => 根元素的大小 => 布局视口
既然自适应与dpr无关那么就可以扩展出很多方案了
4. 其他方案
1.在布局视口等于设备宽度时,直接把根元素字体大小绑定到设备宽度大小上
document.documentElement.style.fontSize = (screen.width/ 6.4) +'px';
这里有关相关的文章 基于screen.width的伪响应式开发
2.直接定死布局视口
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=640, user-scalable=no">//定死为设计稿的尺寸
<meta name="renderer" content="webkit">
<title>定死布局视口</title>
<style>
html, body {
margin: 0
}
div {
width: 200px;
height: 200px;
background: red;
}
body {
background: blue;
}
</style>
<script>
</script>
</head>
<body>
<div id="size200px">元素大小200px</div>
</body>
</html>
不用rem单位,不用设置js,但是布局视口定死后,就不能用css媒体查询做响应式了,从这里也可以看出viewport属性的作用,就是让布局视口通过缩放来适配屏幕宽度,width=device.width仅仅是让布局视口初始大小等于设备宽度,后面设置的initial-scale是用来缩放布局视口大小,而且默认是布局视口初始大小等于设备宽度,也就是所谓的理想视口,换个说法就是如果你设置了initial-scale你可以不用设置 width=device.width了,淘宝方案你把width=device.width去掉,并不会影响自适应过程,加上主要是防止一些不按规范的浏览器出现兼容问题。如果还不能理解viewport的作用,那么可以参考svg中的viewport和viewBox的关系,原理是一样的。
3.使用新出单位 vm, vm 就是专门为自适应而出现的,100vm就是布局视口的宽度,非常厉害,你也不用设置js了
200px: 640px => xvm : 100vm
x=200 * 100 / 640 = 31.25vm
流程 vm => 布局视口
看一下兼容性
兼容还可以,这里也有相关的资料 分享手淘过年项目中采用到的前端技术
5.总结
移动端尺寸自适应与dpr无关,除了淘宝方案外,其他方案都得处理1px的问题,但也减少针对不同dpr设备做响应式处理的麻烦,而且其中也没有一种一劳永逸的方案能解决全部问题。而作为新出来的单位vm,是时候该入坑了
参考文章:
1. 张鑫旭 [基于screen.width的伪响应式开发][10]
2. 大漠 [分享手淘过年项目中采用到的前端技术][11]
3. [flexible][12]
4. 张鑫旭 [设备像素比devicePixelRatio简单介绍][13]
真的,移动端尺寸自适应与dpr无关的更多相关文章
- 最佳移动端h5自适应rem适配方案
一.利用lib-flexible.postcss-plugin-px2rem插件 进行移动端rem适配. 1.第一 引入lib-flexible . 安装lib-flexible: npm i lib ...
- 从原理到方案,一步步讲解web移动端实现自适应等比缩放
前言 在移动端做自适应,我们常用的有媒体查询,rem ,em,宽度百分比这几种方案.但是都各有其缺点. 首先拿媒体查询来说,在某一个宽度区间内只能使用一种样式,为了适应不同屏幕要,css的代码量就会增 ...
- 移动端 解决自适应 和 多种dpr (device pixel ratio) 的 [淘宝] 解决方案 lib-flexible
其实H5适配的方案有很多种,网上有关于这方面的教程也非常的多. 不管哪种方法,都有其自己的优势和劣势. 为什么推荐使用Flexible库来做H5页面的终端设备适配呢? 原理 简单易懂 源码疑问 ...
- 移动端rem自适应设置
对于移动端自适应各种终端的解决方案较多,本篇只是选择其中一种rem适配,我个人做移动端最喜欢的方案. rem就是以html根元素的字体大小为参考,比如html:font-size:20px;1rem= ...
- 移动端webapp自适应实践(css雪碧图制作小工具实践)图文并茂
为什么要写这个 以前写过关于webapp自适应屏幕的文章(链接),不过写的大多数群众看不懂,所以来个图文并茂的版本.虽然只是一个简单的页面,不过在做的过程中也遇到了一些问题,也算是好事吧! 该示例gi ...
- 原生js移动端字体自适应方案
自从进入新公司之后,就一直采用800的方案,也就是判断屏幕尺寸,大于800px是一种html字体处理方案,另一种方案是小于800px的html字体处理方案, 代码如下: (function(doc, ...
- 移动端rem自适应方案
一般设计师给我们的设计稿尺寸都为750*1340 .. 网易,淘宝移动端首页上html元素的font-size 目前就先说一下网易的做法 引入下面这段js,用于计算动态的font-size (func ...
- h5移动端设备像素比dpr介绍
首先介绍一下概念 devicePixelRatio其实指的是window.devicePixelRatio window.devicePixelRatio是设备上物理像素和设备独立像素(device- ...
- 移动端web自适应解决方案: adaptive.js
代码有更新,最好直接查看github github:https://github.com/finance-sh/adaptive adaptivejs利用rem解决移动端页面开发的自适应问题 页面模板 ...
随机推荐
- 解决Windows2003 Server终端服务120天限制
用过windows server 2003做服务器的人都知道,windows2003的性能安全性比以前的windows版本高出很多,但是也带来很多麻烦.其中服务器最重要的远程管理“终端服务”居然要求授 ...
- Yii 1.1 常规框架部署和配置
<?php //index.php //入口文件配置操作 // change the following paths if necessary $yii=dirname(__FILE__).'/ ...
- 相同name,取最小的id的值,mysql根据相同字段 更新其它字段
id name info1 a 1232 a 2353 a 1244 b 125 b 987相同name,取最小的id的值id name info1 a 1232 a 1233 a 1234 b 12 ...
- Apache启动报错:Invalid command 'AuthType', perhaps misspelled or defined by a module not included in it
在apache配置文件里面加了AuthType PFApacheAgent,,结果重启apache的时候歇菜了,,总是报上面的错, <Directory />AllowOverride n ...
- Malformed UTF-8 characters, possibly incorrectly encoded 或中文乱码 (Uncaught InvalidArgumentException: Malformed UTF-8 characters, possibly incorrectly encoded in)
问题: Uncaught InvalidArgumentException: Malformed UTF-8 characters, possibly incorrectly encoded in 是 ...
- KEIL建立新唐MCU的工程时,移植官网程序报错变量未定义问题解决方法
最近在使用新唐的MCU,新唐的MCU使用还算方便,你安装好KEIL之后再安装 Nu-Link_Keil_Driver_V3.00.6909 驱动即可建立新唐的MCU工程,注意的是因为新唐MCU是C51 ...
- PHP安装-centos7
下载地址:https://www.php.net/downloads.php 1.wget下载php源码至/usr/local/src 下 wget https://www.php.net/distr ...
- Fira Code,可以让不等号!=直接显示出来的字体
今天看B站某直播间有人写代码C#里一堆不等号直接显示,感觉很神奇,以为是插件还是什么新语法,托人问了下原来是Fira Code字体 https://github.com/tonsky/FiraCode ...
- JQ获取当前根目录
function getRootPath_web() { //获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp ...
- Scala函数高级操作
字符串高级操作:***** 非常重要 将函数赋值给变量/值def sayHello(name:String): Unit = { println(s"Hello:$name")} ...