首先从屏幕开始说起.
屏幕是由一个一个显示单元组成的.
1 每一个显示单元都是物理世界真实存在的;
2 把一个显示单元的大小称为一个’物理像素’;
3 通常我们所说的 ‘分辨率’, 就是指一块屏幕显示单元的个数, 比如750*1334
, 表示这块屏幕由 750*1334
个显示单元组成
映射规则
像素是计算机系统里面的单位, 通常情况下, 我们让一个像素对应一个显示单元. 所以有时候, 我们说屏幕高 667px, 实际上就是说, 屏幕的的高有 667个显示单元的高度之和.
随着技术的进步, 显示单元可以做的越来越小, 比如以前是 10mm*10mm
的一个显示单元, 现在我们可以做到 5mm*5mm
一个显示单元.
为什么追求显示单元的小? 因为越小图像越精细.
但是: 显示单元的变小, 意味着屏幕的分辨率变大。
这里就牵涉到了一些事情:
假设屏幕的大小不变, 但是分辨率从 A, 变成 2A (也就是显示单元缩小了一半)
并且: 一个像素对应一个显示单元, 这个规则始终不变
此时, 你原来宽度为 100px 的一个元素, 在这个 2A 屏幕上渲染出来, 你会明显的发现:
在视觉上: 这个 100px 明显比之前小了, 和之前的 50px 的时候一样大小.
那怎么办啊, 这样显示肯定是不可以的, 所以我们要对这个情况做处理:
1 我们规定, 大小为 n*n 的显示单元, 是标准的显示单元, 标准意味着它合乎我们长久的判断: 100px 在物理世界大概有多大.
2 我们要知道当前屏幕的显示单元, 和标准显示单元之间的大小比例,比如说当前屏幕的显示单元的大小是标准的一半还是 三分之一.
通过 devicePixelRatio 属性来获
我们可以认为:
devicePixelRatio 标记是: 标准显示单元/当前设备的显示单元
建立在上面的基础上面, 你就可以动态的调整元素的大小, 比如说某个元素 x 的宽度是 100px;
在 devicePixelRatio = 1 的设备上面宽度是 100px
在 devicePixelRatio = 2 的设备上面宽度就要是 200px;
ok, 那么我们来搞.
根据不同的 devicePixelRatio 来调整元素的样式.
var box = document.querySelector('.box');
var height = parseInt(getComputedStyle(box).height);
var width = parseInt(getComputedStyle(box).width);
box.style.height = height * parseInt(window.devicePixelRatio) + 'px';
box.style.width = width * parseInt(window.devicePixelRatio) + 'px';
这仅仅是一个元素的两个属性, 1000个元素, 每个元素 5 个属性, 就可以让你哭掉了.
所以这种处理方式肯定是不可以的.
然后我们发现了 rem 单位.
它的简单解释:
当你给某个元素A 设置了 height:2rem 的时候
它会找到根节点(html) 的 font-size 值, 比如是 16px
然后拿 16 * 2 = 32px
作为元素A 的最终 height.
这个就可以利用了
1 让元素使用 rem 作单位
2 然后控制根元素的 font-size 值, 在不同的 devicePixelRatio 下面的时候, 呈现不同的值
比如你可以设置:
devicePixelRatio = 1, font-size(root) = 100px;
devicePixelRatio = 2, font-size(root) = 200px;
元素在这个时候, 就会自动响应大小的变化.
好, 开始搞:
var fontSize = 100 * parseInt(window.devicePixelRatio) + 'px';
document.documentElement.style.fontSize = fontSize;
嗯, 结果还是不错的, 在不同的分辨率下面, 我们也能实现页面相同了.
实际上, 上面的讨论, 已经解决了我们的问题:
在相同物理尺寸下的设备, 如何在分辨率不同的情况下, 让一个 100px 的元素, 它对应的物理世界的
大小, 始终相同?
现在更近一步, 上面的讨论, 固定了一个变量: 屏幕尺寸, 现在放开这个变量, 固定屏幕的分辨率这个变量.
这个问题就变成适配问题了:
场景描述:
比如你的一个页面本来是以 375宽度为基础做出来的, 那么在设备的宽度变成 320px 的时候,
你的页面就会出现问题: 挤压, 变形, 错乱, 或者超出隐藏, 超出滚动等等操作.
怎么办啊?
希望的是在 320 也能正常显示: 让页面上的所有元素都缩小一些, 也就ok了. 比如一个元素
在 375 设备上面显示这么大, 在 320 上面显示成这么大不就行了.
那么如何缩小?
rem;
你想下, 只要在屏幕的宽度变小的时候, 让根元素的 font-size 跟着变小, 那么所有使用 rem 作为单位的
元素, 是不是也跟着变小, 目标就达成了.
那么怎么让 font-size(root) 随着屏幕的宽度变小而变小啊.
选一对基准值, 比如: 375px/100px; 表示屏幕宽度为 375的时候, font-size(root) 为 100;
每次计算一下就好, 比如发现屏幕的当前宽度为 320, 那么算不出来此时的 font-size(root) 吗??
算出来不会设置根元素的 font-size 吗?
好吧, 上面说的暂时都不要试, 先提一个事情.
所有的上面的讨论, 实际上都建立在:
当你屏幕的分辨率是 100100 的时候, 你就拥有一份 100100 大小的容器, 用来呈现你的网页.
比如说, 你的 iPhone7 的分辨率是 6671334, 那么你就拥有一份 6671334 大小的容器来放你的网页
可惜并不是这样的.
从 iPhone 发布前夕说起:
开发人员发现, 原本为 pc 开发的网页
在 iPhone 上面显示不全, 这部分可以通过滚动条来解决.
但是使用 百分比布局的页面就坑爹了, 原本在 pc 端浏览器上拥有
的 20% 在 iPhone 上面就一点点了, 布局完全乱了, 坑啊.
为了解决这个问题, 开发人员提出了一个的新的玩意: 'layout viewport'
我该怎么解释这个玩意呢.
============== // 这个是你的百分比页面所基于的宽度
=== // 这个是你屏幕的宽度
这样一来, 页面肯定会错乱. 所以提出的 layout viewport 把模型变成这样:
============== // 这个是你的百分比页面所基于的宽度
============= // layout viewport 的宽度
=== // 这个是你屏幕的宽度
你的页面会被放到 layout viewport 这个容器上面, 然后再将 layout viewport 缩小到
和屏幕宽度一样的大小.
并且允许用户放大页面,通过滚动条滑动来浏览器全部页面.
在最初的时候, 这种方式的确解决了 pc 端页面在手机上浏览的问题, 但是随着移动端的兴起,
大量的针对移动端的页面被制作出来, 也就是模型变成这样:
=== // 针对移动端做的页面
============ // layout viewport
=== // 屏幕的宽度
这样很明显就出现问题了: 你的页面先放到 layout viewport 上面, 然后又缩小到和屏幕宽度一致
最终显示出来的, 就是你的页面明显被缩小了.
所以我们要解决这个问题, 要把 layout viewport 的大小, 变成和屏幕的宽度一致.
这里假设屏幕的显示单元始终是标准的显示单元大小。
怎么让 layout viewport 变成和屏幕的宽度一致呢?
通过 meta name=”viewport” 标签.
解释一下:
meta name=”viewport” 有一个 content 属性, 它里面有几个值, 可以用来对 layout viewport
做处理. content 有如下几个字段:
initial-scale: 这个值会影响最终 layout viewport 的宽度, 计算公式应该是这样:
屏幕的分辨率/(devicePixelRatio*initial-scale) = 最终的 layout viewport 的宽度.
屏幕的分辨率我们可以拿到, devicePixelRatio 也可以拿到.
比如 iPhone7, 屏幕分辨率是 750*1334, devicePixelRatio=2, 当你设置 initial-scale=1 的时候
layout viewport 的最终宽度就是 375;
这里有一个点, 我说一下;
我们可以让 layout viewport 的宽度是任意值, 通过对 initial-scale 的设置.
那我们要设置它为多少呢?
可以设置成 375, 这个宽度, 是以标准显示单元为单位算出来的宽度
也可以设置成 750, 这样的话, 你的 1px 就完整对应这个设备的 1 个显示单元.
我们选择后者, 因为这个牵涉到 1px border 的实现.
如果设置成这个, 那么你的 initial-scale 始终只要设置成 1/devicePixelRatio 即可,
因为 devicePixelRatio * 1/devicePixelRatio = 1;
还有其他的两个相关属性:
maximum-scale: 最大能放大多少
minimum-scale: 最小能放大多少
希望不能缩放, 因为我们的页面不需要缩放就能正常显示, 缩放了反而显示不正确。
最终统筹一下:
我们要做的事情
1 让 layout viewport 变成和屏幕分辨率一致的宽度
2 根据设备宽度和 devicePixelRatio 来指明根元素的 font-size 值
这些操作之后, 你就可以实现最终的代码了.