一, 设备像素比 dpr (devicePixelRatio)
首先进行概念的一些解释:
1, 物理像素(又称硬件像素)(physical pixel)
一个物理像素是显示器上最小的物理显示单元, 每一个像素都有自己的颜色值和亮度.
同一个设备, 它的物理像素是固定的, 即一个设备的分辨率是固定的.
- // 计算公式:
- // 物理像素 = 逻辑像素 × 设备像素比
2, 逻辑像素 dpi (又称 CSS 像素, 设备独立像素)(density-independent pixel)
逻辑像素是指 css 样式中使用的逻辑像素, 也可以说是设备的宽高.
iphone4 之前的手机逻辑像素数和逻辑像素数是相等的, 但是由于视网膜设备 (Retina 屏) 的出现, 逻辑像素与物理像素之间有了不同的倍数对等关系.
- // 获取方法
- document.documentElement.clientWidth //iphone6 => 320
- document.documentElement.clientHeight //iphone6 => 667
3, 设备像素比 dpr (devicePixelRatio)
设备像素比 = 物理像素 / 逻辑像素
- //js 获取方法
- window.devicePixelRadio //iphone6 =>2
注意点:
(1)在 fireFox 及 ie 中不支持 window.devicePixelRatio
(2)在同一设备不同浏览器中, window.devicePixelRatio 值可能不同, 这是由于不同的浏览器中 css 像素数不同
4, 像素密度 ppi (pixels per inch)
每英寸中显示的像素数, 通常使用 ppi 来作为像素密度的单位; 计算公式: 对角线像素个数 / 屏幕尺寸
对角线像素个数计算:(物理像素长 ² + 物理像素宽 ²)开根
不同手机物理像素, 逻辑像素及像素比:
手机名称 | 像素分辨率(px) | 倍率 | 逻辑分辨率 (pt) | 物理尺寸 (英寸) | 屏幕密度 (ppi) | DPI |
i6 plus | 1242*2208 | @3x | 414*736 | 5.5 | 401 | 154 |
I6 | 750*1334 | @2x | 375*667 | 4.7 | 330 | 163 |
I5s | 640*1136 | @2x | 320*568 | 4 | 330 | 163 |
I5 | 640*1136 | @2x | 320*568 | 4 | 330 | 163 |
I4 | 640*960 | @2x | 320*480 | 3.5 | 330 | 163 |
I3GS | 320*480 | @1x | 320*480 | 3.5 | 163 | |
android | 240*320 | @0.75 | 320*420 | 120 | ||
android | 320*480 | @1x | 320*480 | 160 | ||
android | 480*800 | @1.5x | 360*500 | 240 | ||
android | 640*960 | @2x | 320*480 | 320 | ||
android | 540*960 | @1.5x | 360*640 | 360 | ||
android | 720*1280 | @2x | 360*640 | 360 | ||
android | 1080*1920 | @3x | 360*640 | 360 |
综合以上几个概念, 以 Iphone6 举例说明: 设备宽高 (即逻辑像素) 为: 375×667 ,dpr 为 2, 物理像素为 750×1334
- //css 样式
- width:1px;
- height:1px;
相同尺寸下, 普通屏幕 VS Retina 屏, css 像素所呈现的物理尺寸 (大小) 是一致的, 不同的是一个 css 像素所对应的物理像素的个数不一致:
普通屏幕: css 像素: 物理像素 = 1:1
retina 屏: css 像素: 物理像素 = 1:4
即 4 个物理像素显示一个 css 像素;
二, 图片高清显示
首先了解一下图片的显示规则:
位图像素: 一个位图像素是栅格图像最小的数据单元. 每个位图像素都包含自身的显示信息, 如色值, 透明度等
理论上, 当位图像素对应物理像素时, 图片才能清晰展示.
例如: img 标签, css 像素为 200*300, 普通屏幕下对应物理像素为 200*300,retina 屏幕对应物理像素为 400*600:
(1)当在 retina 屏中, 使用 200*300@1x 图片时, 由于每个单位的位图像素不可分割, 只能就近取色, 从而导致 retina 屏图片模糊; 使用两倍图片 @2x, 如 200*300 的 img 标签, 使用 400*600 的图片, 这样 retina 屏幕下, 物理像素: 位图像素 = 1:1, 图片清晰
(2)如果在普通屏下使用 @2x 图片, 一个物理像素对应 4 个位图像素, 它的取色需要一定的算法, 显示结果就只有原图像素总数的 4 分之一(这个过程叫 downsampling), 图片虽然不会模糊, 但是会少一些锐利度或者色差; 同样会造成资源浪费
最好的解决方案: 不同的 dpr 下, 使用不同尺寸的图片.
(1)可以通过媒体查询或者 js 进行控制, 拼接不同的 url
(2)img 标签, 设置 srcset 属性
- //srcrest 以最合适的 src 去匹配不同屏幕(高分屏低分屏如 Retina; 大屏小屏)
- // 2x,3x 表示目标屏幕的像素密度
- <img src="source.jpg" srcset="source_2x.jpg 2x, source_3x.jpg 3x">
- //400w,600w 表示目标浏览器的宽度的限度, 如浏览器宽度 550w 时, 匹配 600w 的 src
- <img src="source.jpg" width="100%" srcset="source_400.jpg 400w, source_600.jpg 600w, source_1280.jpg 1280w">
二, 移动端适配之 rem
适配前提:
<meta name="viewport" content="initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width">
设配原理: 使用 rem 单位进行移动端布局适配, 原理是针对不同手机屏幕尺寸和 dpr 动态的改变根节点 html 的 font-size 大小
1,js 方法实现
(1)引入 flexible.js, 改变根元素 font-size
- ;(function(win, lib) {
- var doc = win.document;
- var docEl = doc.documentElement;
- var metaEl = doc.querySelector('meta[name="viewport"]');
- var flexibleEl = doc.querySelector('meta[name="flexible"]');
- var dpr = 0;
- var scale = 0;
- var tid;
- var flexible = lib.flexible || (lib.flexible = {});
- if (metaEl) {
- console.warn('将根据已有的 meta 标签来设置缩放比例');
- var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
- if (match) {
- scale = parseFloat(match[1]);
- dpr = parseInt(1 / scale);
- }
- } else if (flexibleEl) {
- var content = flexibleEl.getAttribute('content');
- if (content) {
- var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
- var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
- if (initialDpr) {
- dpr = parseFloat(initialDpr[1]);
- scale = parseFloat((1 / dpr).toFixed(2));
- }
- if (maximumDpr) {
- dpr = parseFloat(maximumDpr[1]);
- scale = parseFloat((1 / dpr).toFixed(2));
- }
- }
- }
- if (!dpr && !scale) {
- var isAndroid = win.navigator.appVersion.match(/android/gi);
- var isIPhone = win.navigator.appVersion.match(/iphone/gi);
- var devicePixelRatio = win.devicePixelRatio;
- if (isIPhone) {
- // iOS 下, 对于 2 和 3 的屏, 用 2 倍的方案, 其余的用 1 倍方案
- if (devicePixelRatio>= 3 && (!dpr || dpr>= 3)) {
- dpr = 3;
- } else if (devicePixelRatio>= 2 && (!dpr || dpr>= 2)){
- dpr = 2;
- } else {
- dpr = 1;
- }
- } else {
- // 其他设备下, 仍旧使用 1 倍的方案
- dpr = 1;
- }
- scale = 1 / dpr;
- }
- docEl.setAttribute('data-dpr', dpr);
- window.dpr = dpr;
- if (!metaEl) {
- metaEl = doc.createElement('meta');
- metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', '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);
- }
- }
- function refreshRem(){
- var width = docEl.getBoundingClientRect().width;
- if (width / dpr> 540) {
- width = 540 * dpr;
- }
- var rem = width / 10;
- docEl.style.fontSize = rem + 'px';
- flexible.rem = win.rem = rem;
- }
- win.addEventListener('resize', function() {
- clearTimeout(tid);
- tid = setTimeout(refreshRem, 300);
- }, false);
- win.addEventListener('pageshow', function(e) {
- if (e.persisted) {
- clearTimeout(tid);
- tid = setTimeout(refreshRem, 300);
- }
- }, false);
- // if (doc.readyState === 'complete') {
- // doc.body.style.fontSize = 12 * dpr + 'px';
- // } else {
- // doc.addEventListener('DOMContentLoaded', function(e) {
- // doc.body.style.fontSize = 12 * dpr + 'px';
- // }, false);
- // }
- refreshRem();
- flexible.dpr = win.dpr = dpr;
- flexible.refreshRem = refreshRem;
- flexible.rem2px = function(d) {
- var val = parseFloat(d) * this.rem;
- if (typeof d === 'string' && d.match(/rem$/)) {
- val += 'px';
- }
- return val;
- }
- flexible.px2rem = function(d) {
- var val = parseFloat(d) / this.rem;
- if (typeof d === 'string' && d.match(/px$/)) {
- val += 'rem';
- }
- return val;
- }
- })(window, window['lib'] || (window['lib'] = {}));
(2)css 使用 rem 为单位
css 代码中如何还原视觉稿尺寸: 视觉稿一般均为二倍视觉稿, 例如: 以 iphone6(设备尺寸 375*667)做设计标准, 高清设计稿为 750 * 1334,html 的 font-size 会被设置为 75px
方法一: 利用 sass/less @mixin 计算
- @mixin px2rem($attr,$value){
- $attr:($value / 75) * 1rem
- }
- p{ // 设计稿 300* 300 的元素
- @include px2rem(width,300);
- @include px2rem(height,300) ;
- }
方法二: 打包工具 postcss-px2rem
css 代码中按照设计稿尺寸编写, css 文件利用打包工具, 打包为 rem 单位
- //gulpfile.babel.js
- // 具体使用方法请参考官网
- px2rem({
- remUnit:75 // 设计稿尺寸 / 10
- }),
注意:
使用 rem 为单位, 1px 边框转化为 rem 后, 在 andriod 等版本手机, 由于实际计算边框宽度不足 1px, 所以, 边框不显示;
解决方法:(1)增加边框宽度, 经实测 2.5px 边框, ios 和 andriod 手机效果兼容性更好
(2)在 sass 文件中, 在不需要转化 rem 的地方, 添加注释 /*no*/
- .testDiv{
- border:1px solid red;/*no*/
- height:100px;
- line-height:100px;
- font-size:75px;
- }
2,css 媒体查询实现
- html{
- font-size:32px;
- }
- @media (min-device-width:320px){
- html{
- font-size:32px;
- }
- }
- @media (min-device-width:375px){
- html{
- font-size:37.5px;
- }
- }
- @media (min-device-width:414px){
- html{
- font-size:41.4px;
- }
- }
缺点: 根据设备宽度媒体查询设置根元素 font-size, 不够准确, 只能设置某一范围, 赋予相同字号
来源: https://www.cnblogs.com/zuozuo-blog/p/8986543.html