首先
屏幕是由一个一个显示单元组成的.
1 每一个显示单元都是物理世界真实存在的;
2 把一个显示单元的大小称为一个’物理像素’;
3 通常我们所说的 ‘分辨率’, 就是指一块屏幕显示单元的个数, 比如 750 叉 1334, 表示这块屏幕由 750叉1334 个显示单元组成
其次
通常情况下, 1 个显示单元对应 计算机系统中的 1px.
也就是说, 如果你设置了一个元素的 height:100px; 在屏幕中会有 100个显示单元来渲染它.
后来出现了一种情况
在相同大小的屏幕下,
屏幕分辨率不一样, 一个分辨率是 A, 而另外一个分辨率是 2A — 因为我们可以把显示单元做的更小了
这种情况的出现, 有如下的影响
如果我们维持着: 计算机系统中的 1px, 对应物理上的 1个显示单元.
那么同样一个页面, 在 A 显示正常, 在 2A 的情景下面, 就只会显示一半.
这种情况肯定是不可以的.所以我们需要针对这种情况做处理: 浏览器提供了一个 devicePixelRatio(设备像素比) 的属性, 用来标记:
标准显示单元的大小/当前设备的显示单元的大小
并且明确两条规则:
1px 始终对应 1个显示单元
标准的 1个显示单元大小为 x, 其他的显示单元, 可能是 1/2x, 1/3x
我们可以通过判断这个值, 来调整我们使用的 px 的大小,
比如:
devicePixelRatio = 1 的设备中, 元素a 的宽度为 100px;
devicePixelRatio = 2 的设备中, 元素a 的宽度为 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.
这个就可以利用了
让元素使用 rem 作单位
然后控制根元素的 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;
嗯, 结果还是不错的, 在不同的分辨率下面, 我们也能实现页面相同了.
然后你会总是觉得, pc 上面的 100px, 和你 devicePixelRatio=2 的时候的 200px
的大小不一致的, 按道理来说应该是一致的.
的确不一致.
先明确一个概念
浏览器可视区域(visual viewport)
我们之前说了 ‘计算机系统中的 1px 始终对应 1个物理显示单元’
那么对应 750*1334 分辨率的屏幕, 我们同样可以这么描述它:
屏幕的大小为 750px*1334px.
前面已经说过了, 相同物理尺寸的屏幕, 分辨率可能不同, 因为显示单元的个数不同.
所以这里的 750px, 可能仅仅是标准下面的 375px;
另外一个概念:ideal viewport
它表示的是说:
当前设备, 使用标准显示单元为单位的时候的大小.
比如说 750*x 的分辨率, devicePixelRatio = 2,
那么它在标准显示单元下面, 宽度就是 375px * x/2
最后我们提一下 layout viewport, 这是为什么大小不一致的原因:
历史:
从 iPhone 发布前夕说起, 开发人员发现, 原本为 pc 开发的网页
在 iPhone 上面显示不全, 这部分可以通过滚动条来解决.
但是使用 百分比布局的页面就坑爹了, 原本在 pc 端浏览器上拥有
的 20% 在 iPhone 上面就一点点了, 布局完全乱了, 坑啊.
为了解决这个问题, 开发人员提出了一个的新的概念: 'layout viewport'
layout viewport的默认大小为 980px, 并且默认缩放到和 visual viewport 区域一般大小.
在这种情况下, 我们可以计算出layout viewport下,
一个 100px 宽度的元素, 对应的 visual viewport 下面的宽度 x
layout viewport / visual viewport = ele-width(layout viewport) / x
也就是
x = ele-width(layout viewport) * (layout viewport / visual viewport);
ideal viewport 下面的宽度, 只要再除以 devicePixelRatio 即可.
很明显的看出来:
width(layout viewport) = width(visual viewport) 的时候, 两个 viewport 中的元素宽度值
是相等的.width(visual viewport) / devicePixelRatio = ideal viewport 中的元素的大小.
而我们的最终追求, 就是
当你写下 100px 的时候, 在任何 devicePixelRatio 下面的大小都是一致的.
要做到这一点, 就要做到它们的 ideal viewport 下面的大小始终一致的.
而一个元素在 ideal viewport 下的大小的计算公式为:
( ele-width(layout viewport) * (layout viewport / visual viewport) ) / devicePixelRatio;
因为不同的设备的 visual viewport 的值是不同的, 我们可以控制让 layout viewport 的大小始终
等于 visual viewport, 这样比例始终为 1devicePixelRatio 在不同的设备中有不同
假设 ele-width(ideal viewport) = x; devicePixelRatio = n; 那么 ele-width(layout viewport) = nx;
所以我们只要保证, ele-width 的宽度, 始终为 nx 即可, 因为通常情况下我们是知道 x 的.
how?
控制 layout viewport 的大小始终等于 visual viewport
通过 meta name=”viewport” 的 content 的 initial-scale 来控制.initial-scale = 1 , layout viewport 的宽度为 375 (同ideal viewport) initial-scale = 2 , layout viewport 的宽度为 188 initial-scale = 0.5, layout viewport 的宽度为 750;
所以得到的结论:
当 initial-scale 的值为 1/devicePixelRatio 的时候,
width(layout viewport) = width(visual viewport)
2 通过 rem, 以及根据不同的 devicePixelRatio 设置 根节点的 font-size 值, 来控制 nx 的值的大小.
然后需要给出一份基准值:
在 750(visual viewport), devicePixelRatio = 2 的时候,
root(font-size) = 200px;
如果都做到这里了, 那么至少可以达到:
在不同的 devicePixelRatio 下面元素的大小都是一致的.
但是依旧存在一个问题, 当前页面是基于 750 (visual viewport) 定义的, 也就是说
当你的设备实际上只有 640(visual viewport) 的时候,
你的整个页面还是 750px, 就会出现滚动条.
所以我们想要等比缩放一下.
如何操作?
直接等比缩放一下 root(font-size) 的值:
750/200 = 640/x
x = 640 / (750 / 200)