3

首先从屏幕开始说起.

屏幕是由一个一个显示单元组成的.
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) 随着屏幕的宽度变小而变小啊.

  1. 选一对基准值, 比如: 375px/100px; 表示屏幕宽度为 375的时候, font-size(root) 为 100;

  2. 每次计算一下就好, 比如发现屏幕的当前宽度为 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 值

这些操作之后, 你就可以实现最终的代码了.


云水摇啊摇
362 声望15 粉丝

不信人间有白头.