可能还有个问题,为什么不直接用百分比来适配?因为百分比在很多情况下是除不尽或者带有小数的,显然带有小数点的px会带来各种各样的误差
高清适配如果你觉得移动端适配像上面一样简单转化下就行,那就 too young too sample
1px问题什么是 1px 问题?
以 iphone6 为例,大家应该听过啥视网膜像素之类的,2倍屏之类的吧。其实也就是此时 设备像素比(dpr) = 设备像素值(dps) / 设备独立像素值(dips),即一个css像素对应两个物理像素,也就是你在css中写的1px其实在设备显示的是两个像素,当你设置 border = 1px 时看起来就没有那种1px的纤细效果,总感觉不尽如人意,差那么一点点味道。
你以为的1px
用户看到的1px(请忽略颜色不同)
追求用户体验的公司通常是不能容忍 1px 问题的
图片的模糊问题同样的以 iphone6 为例,我们如果定义一张图片宽度为375px,如果图片的像素(位图像素),此时一个像素的图片会对应两个物理像素(参考上面的位图像素),就会造成图片模糊的问题了。你可能会问?那我直接加载750px像素的图片不就好了(位图像素大于物理像素时很多人是看不出失真的)。
答案当然是可以的,但你觉得追求用户体验的公司能容忍无故的流量耗费和性能浪费么?当然不能
解决方案 <meta content="width=device-width, initial-scale=1, user-scalable=no">前面也有介绍过这部分代码,但是没有说明 initial-scale=1 的作用,initial-scale 定义了页面的初始缩放,1代表不缩放。initial-scale的值也会影响页面宽度,即此时的css像素。
前面我们说过,在 viewport meta 的约束下
页面宽度 = window.innerWidth = 设备宽度,但其实正确的是 页面宽度 = window.innerWidth = 设备宽度 / scale,为什么是除呢?大家可以想象一下,当页面缩放时(例如scale=0.5),是不是会导致更多的内容内容展示在当前可见区域中,css像素(页面)是变大了。
以 iphone6 为例,当我们设置
<meta content="width=device-width, initial-scale=0.5, user-scalable=no">此时页面宽度 = window.innerWidth = 设备宽度 / scale = 375 / 0.5 = 750px,也就是说现在页面宽度(对应css像素)和物理像素是相等的,所以我们设置的 1px 在手机中将真正显示 1pt(1个物理像素),也就解决了1px的问题。
所以解决方法如下
// 获取设备dpr const dpr = window.devicePixelRatio; // 计算缩放比例 const scale = 1 / dpr; // 动态设置meta const metaEl = doc.createElement('meta'); metaEl.setAttribute('name', 'viewport'); metaEl.setAttribute('content', 'width=device-width,initial-scale=' + scale + ', user-scalable=no');对应图片而言,要想达到最清晰的显示状态则要使图片的位图像素与设备的物理像素对应,所以可以对图片做如下适配
[dpr=1] img {
width: 200rem;
background: '@1x.png';
}
[dpr=2] img {
width: 200rem;
background: '@2x.png';
}
此方案的原理就是利用meta来更过css像素(因为css像素是虚拟像素由计算机定义的,见上文),以此达到一个css像素对应一个物理像素的效果,1px == 1pt
rem高清适配利用上文提供的rem移动端适配思路,加上现在的高清适配思路,就可以完成移动端高清适配啦
直接贴代码,来自前端:『REM』手机屏幕高清适配方案
(function(designWidth, rem2px) { var win = window; var doc = win.document; var docEl = doc.documentElement; var metaEl = doc.querySelector('meta[name="viewport"]'); var dpr = 0; var scale = 0; var tid; if (!dpr && !scale) { var devicePixelRatio = win.devicePixelRatio; if (win.navigator.appVersion.match(/iphone/gi)) { if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { dpr = 3; } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){ dpr = 2; } else { dpr = 1; } } else { dpr = 1; } scale = 1 / dpr; } docEl.setAttribute('data-dpr', dpr); if (!metaEl) { metaEl = doc.createElement('meta'); metaEl.setAttribute('name', 'viewport'); metaEl.setAttribute('content', 'width=device-width,initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no'); if (docEl.firstElementChild) { docEl.firstElementChild.appendChild(metaEl); } else { var wrap = doc.createElement('div'); wrap.appendChild(metaEl); doc.write(wrap.innerHTML); } } else { metaEl.setAttribute('name', 'viewport'); metaEl.setAttribute('content', 'width=device-width,initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no'); } // 以上代码是对 dpr 和 viewport 的处理,代码来自 lib-flexible。 // 一下代码是处理 rem,来自上篇文章。不同的是获取屏幕宽度使用的是 // document.documentElement.getBoundingClientRect // 也是来自 lib-flexible ,tb的技术还是很强啊。 function refreshRem(_designWidth, _rem2px){ // 修改viewport后,对网页宽度的影响,会立刻反应到 // document.documentElement.getBoundingClientRect().width // 而这个改变反应到 window.innerWidth ,需要等较长的时间 // 相应的对高度的反应, // document.documentElement.getBoundingClientRect().height // 要稍微慢点,没有准确的数据,应该会受到机器的影响。 var width = docEl.getBoundingClientRect().width; var d = window.document.createElement('div'); d.style.width = '1rem'; d.style.display = "none"; docEl.firstElementChild.appendChild(d); var defaultFontSize = parseFloat(window.getComputedStyle(d, null).getPropertyValue('width')); // d.remove(); var portrait = "@media screen and (width: "+ width +"px) {html{font-size:"+ ((width/(_designWidth/_rem2px)/defaultFontSize)*100) +"%;}}"; var dpStyleEl = doc.getElementById('dpAdapt'); if(!dpStyleEl) { dpStyleEl = document.createElement('style'); dpStyleEl.id = 'dpAdapt'; dpStyleEl.innerHTML = portrait; docEl.firstElementChild.appendChild(dpStyleEl); } else { dpStyleEl.innerHTML = portrait; } // 由于 height 的响应速度比较慢,所以在加个延时处理横屏的情况。 setTimeout(function(){ var height = docEl.getBoundingClientRect().height; var landscape = "@media screen and (width: "+ height +"px) {html{font-size:"+ ((height/(_designWidth/_rem2px)/defaultFontSize)*100) +"%;}}" var dlStyleEl = doc.getElementById('dlAdapt'); if(!dlStyleEl) { dlStyleEl = document.createElement('style'); dlStyleEl.id = 'dlAdapt' dlStyleEl.innerHTML = landscape; docEl.firstElementChild.appendChild(dlStyleEl); } else { dlStyleEl.innerHTML = landscape; } },500); } // 延时,让浏览器处理完viewport造成的影响,然后再计算root font-size。 setTimeout(function(){ refreshRem(designWidth, rem2px); }, 1); })(750, 100);代码比较多,有兴趣的可以直接上github上找到源代码(https://github.com/hbxeagle/rem/blob/master/HD_ADAPTER.md)
后记这是一篇很早之前写的总结了,今天又复习修改了一下,写的有错误或者写的不清楚的地方请大家多多指正。