wangning0 / Autumn_Ning_Blog

571 stars 119 forks source link

移动端高清、多屏适配方案 #13

Open wangning0 opened 7 years ago

wangning0 commented 7 years ago

一些概念

物理像素(physical pixel)

一个物理像素是显示器(手机屏幕)上最小的物理显示单元,早操作系统的调度下,每个设备像素都有自己的颜色值和亮度值

设备独立像素(density-independent pixel)

设备独立像素(也叫做密度无关像素),可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素(比如:css像素),然后由相关系统转换为物理像素

设备像素比(device pixel ratio)

设备像素比(简称dpi),定义了物理像素和设备独立像素的对应关系,它的值可以按如下的公式得到:

设备像素比 = 物理像素/设备独立像素

在JavaScript中,可以通过window.devicePixelRatio 获取当前设备的dpr


在不同的屏幕上,css像素所呈现的大小是一致的,不同的是1个css像素所对应的物理像素个数是不一致的

在普通屏幕下 1个css像素对应1个物理像素 在retina屏素霞,1个css像素对应4个物理像素

位图像素

一个位图像素是栅格图像(png、jpg、gif等)最小的数据单元,每个位图像素都包含着一些自身的显示信息(显示位置、颜色值、透明度等)


几个问题

retina下,图片高清问题

解决方案可以是两倍图片(@2x),然后图片容器缩小50%

缺点是 在普通屏幕下,会造成资源的浪费,图片会失去一些锐利度

最好的解决方案是 不同的dpr下,加载不同的尺寸的图片,通过css媒体查询或者是通过js条件判断都可以

还有一个就是有一个图片服务器,通过url获取参数,然后可以控制图片质量,也可以将图片裁剪成不同的尺寸

retina下,border 1px问题

可以使用hack,将y轴缩小0.5倍

.scale {
    position: relative;
}
.scale:after {
    content: "",
    position: absolute,
    bottom: 0px,
    left:0px;
    right:0px;
    border-bottom:1px solid #ddd;
    -webkit-transform:scaleY(.5);
    -webkit-transform-origin:0 0;
}

或者是设置meta标签,设置viewport(scale 0.5)

<meta name="viewport" content="width=640,initial-scale=0.5,maximum-scale=0.5, minimum-scale=0.5,user-scalable=no">

页面被scale 必然会带来一些问题

多屏适配布局问题

最好的方案莫过于使用相对单位rem

基于rem的原理:针对不同手机的屏幕尺寸和dpr动态的改变根节点html的font-size大小

rem = document.documentElement.clientWidth * dpr / 10

乘以dpr,是因为页面有可能为了实现1px border页面会缩放(scale)1/dpr,如果没有,dpr=1

除以10,为了取整,方便计算

对于动态改变根节点html的font-size,我们可以通过css做,也可以通过js做

css方式,可以通过设备宽度来媒体查询来改变html的font-size

html{font-size: 32px}
// iphone6
@media (min-device-width: 375px) {
    html{font-size: 64px;}
}
// iphone6 plus
@media (min-device-width: 414px) {
    html {font-size: 75px}
}

这样的做法,没有做到足够的精确,但是够用

javascript方式,通过上面的公式,计算出基准值rem,然后写入样式

var dpr, rem, scale;
var docEl = document.documentElement;
var fontEl = document.createElement('style');
var metaEl = document.querySelector('meta[name="viewport"]');

dpr = window.devicePixelRatio || 1;
rem = docEl.clientWidth * dpr / 10;
scale = 1 / dpr;

// 设置viewport,进行缩放,达到高清效果
metaEl.setAttribute('content', 'width=' + dpr * docEl.clientWidth + ',initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no');

// 设置data-dpr属性,留作的css hack之用
docEl.setAttribute('data-dpr', dpr);

// 动态写入样式
docEl.firstElementChild.appendChild(fontEl);
fontEl.innerHTML = 'html{font-size:' + rem + 'px!important;}';

// 给js调用的,某一dpr下rem和px之间的转换函数
window.rem2px = function(v) {
    v = parseFloat(v);
    return v * rem;
};
window.px2rem = function(v) {
    v = parseFloat(v);
    return v / rem;
};

window.dpr = dpr;
window.rem = rem;

解决了: border: 1px问题 图片高清问题 屏幕适配布局问题

字体大小问题

对于字体缩放问题,设计师的要求是任何手机屏幕上字体大小都要统一,所以我们针对不同的分辨率(dpr不同),会做如下处理

font-size: 16px;
[data-dpr="2"] input {
    font-size: 32px;
}

注意,字体不可以用rem,误差太大了,且不能满足任何屏幕下字体大小相同