概念
rem
相对长度单位,相对于根元素(即html元素)font-size计算值的倍数
Element.clientWidth
属性表示元素的内部宽度,以像素计。该属性包括内边距,但不包括垂直滚动条(如果有)、边框和外边距。
Document.documentElement
是一个会返回文档对象(document)的根元素的只读属性(如HTML文档的 元素)。
window.devicePixelRatio
此属性返回当前显示设备的物理像素分辨率与CSS像素分辨率的比值。该值也可以被解释为像素大小的比例:即一个CSS像素的大小相对于一个物理像素的大小的比值。
视窗 viewport
简单的理解,viewport是严格等于浏览器的窗口。在桌面浏览器中,viewport就是浏览器窗口的宽度高度。但在移动端设备上就有点复杂。 移动端的viewport太窄,为了能更好为CSS布局服务,所以提供了两个viewport:虚拟 visualviewport和布局的layoutviewport
物理像素(physical pixel)
物理像素又被称为设备像素,他是显示设备中一个最微小的物理部件。每个像素可以根据操作系统设置自己的颜色和亮度。正是这些设备像素的微小距离欺骗了我们肉眼看到的图像效果
设备独立像素(density-independent pixel)
设备独立像素也称为密度无关像素,可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素(比如说CSS像素),然后由相关系统转换为物理像素。
CSS像素==设备独立像素
CSS像素是一个抽像的单位,主要使用在浏览器上,用来精确度量Web页面上的内容。一般情况之下,CSS像素称为与设备无关的像素(device-independent pixel),简称DIPs。
Document: DOMContentLoaded 事件
当纯HTML被完全加载以及解析时,DOMContentLoaded 事件会被触发,而不必等待样式表,图片或者子框架完成加载。 在你的脚本有机会运行前,DOMContentLoaded可能就已经被触发。所以你在决定添加一个事件监听器前最好先检查一下。
function doSomething() {
console.info('DOM loaded');
}
if (document.readyState === 'loading') { // 此时加载尚未完成
document.addEventListener('DOMContentLoaded', doSomething);
} else { // 此时`DOMContentLoaded` 已经被触发
doSomething();
}
document.readyState
一个document 的 Document.readyState 属性描述了文档的加载状态。
当该属性值发生变化时,会在document 对象上触发readystatechange事件。 一个文档的 readyState 可以是以下之一:
- loading / 正在加载 document 仍在加载。
- interactive / 可交互 文档已被解析,"正在加载"状态结束,但是诸如图像,样式表和框架之类的子资源仍在加载。
- complete / 完成 文档和所有子资源已完成加载。表示 load 状态的事件即将被触发。 当这个属性的值变化时,document 对象上的readystatechange 事件将被触发。
pageshow
当一条会话历史记录被执行的时候将会触发页面显示(pageshow)事件。(这包括了后退/前进按钮操作,同时也会在onload 事件触发后初始化页面时触发)
window.addEventListener('pageshow', function(event) {
console.log('pageshow:');
console.log(event);
});
persisted
The persisted read-only property indicates if a webpage is loading from a cache.
设备像素比(device pixel ratio)
设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系。它的值可以按下面的公式计算得到:
设备像素比 = 物理像素 / 设备独立像素
在JavaScript中,可以通过window.devicePixelRatio获取到当前设备的dpr。而在CSS中,可以通过-webkit-device-pixel-ratio,-webkit-min-device-pixel-ratio和 -webkit-max-device-pixel-ratio进行媒体查询,对不同dpr的设备,做一些样式适配(这里只针对webkit内核的浏览器和webview)。
众所周知,iPhone6的设备宽度和高度为375pt * 667pt,可以理解为设备的独立像素;而其dpr为2,根据上面公式,我们可以很轻松得知其物理像素为750pt * 1334pt
开始适配
首先通过设置meta,其主要作用的是width=device-width,使用这个之后,document.documentElement.clientWidth就等于设备独立像素的宽度。
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
然后给root元素设置fontSize为document.documentElement.clientWidth的十分之一,这样1rem就等于document.documentElement.clientWidth/10,以此做适配。
rem并非是完美的适配方案,使用了rem,最后渲染时还是转换成px,这时小数部分就四舍五入,有些结果并不是我们想要的。
代码:
(function flexible(window, document) {
var docEl = document.documentElement // 返回文档的root元素
var dpr = window.devicePixelRatio || 1 // 获取设备的dpr,即当前设置下物理像素与虚拟像素的比值
// adjust body font size
// 调整body标签的fontSize,fontSize = (12 * dpr) + 'px'
// 设置默认字体大小,默认的字体大小继承自body
function setBodyFontSize() {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10
// 设置root元素的fontSize = 其clientWidth / 10 + ‘px’
function setRemUnit() {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize
// 当页面展示或重新设置大小的时候,触发重新
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
// detect 0.5px supports
// 检测0.5px的支持,支持则root元素的class中有hairlines
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
针对华为,小米的部分机型,微信内置浏览器产生的rem不能正确填充满的问题,产生这个情况的原因是因为给html附font-size时,附上的font-size和实际上html的font-size 大小并不一致
尝试找了多个问题机型,最终的比例都是1.25左右(1.24999),所以解决方案如下
~function () {
let docEl = document.documentElement;
// set 1rem = viewWidth / 10
// 设置root元素的fontSize = 其clientWidth / 10 + ‘px’
function setRemUnit() {
var rem = docEl.clientWidth / 10
let real_font=parseFloat(window.getComputedStyle(document.getElementsByTagName("html")[0]).fontSize.split('p')[0])
//检测部分华为手机fontsize问题
if(real_font*120*100<rem*10000){
docEl.style.fontSize = rem*100*125/10000 + 'px'
}
}
setRemUnit()
// reset rem unit on page resize
// 当页面展示或重新设置大小的时候,触发重新
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
}(window, document)