对于移动端开发而言,为了做到页面高清的效果,视觉稿的规范往往会遵循以下两点:
1)选取一款手机的屏幕宽高作为基准(比如 iphone6的375×667)。
2)对于高清屏幕,为了达到高清效果,视觉稿的画布大小会是基准的2倍(对iphone6而言:原先的375×667,就会变成750×1334)。
问题1:对于dpr=2的手机,为什么画布大小×2,就可以解决高清问题?
首先,我们要先了解一下 dpr 是什么?
- 设备像素比(简称dpr)定义了物理像素和设备独立像素的对应关系,它的值可以按如下的公式的得到:
设备像素比 = 物理像素 / 设备独立像素 // 在某一方向上,x方向或者y方向
- 一个物理像素是显示器(手机屏幕)上最小的物理显示单元,在操作系统的调度下,每一个设备像素都有自己的颜色值和亮度值。
- 设备独立像素,这个点代表一个可以由程序使用的虚拟像素(比如: css像素),然后由相关系统转换为物理像素。
总而言之,就是不同的设备(不同的系统),他们有不同的dpr,这些设备在拿到前端写好的页面(即css像素布局)之后,会根据dpr占用不同数量的物理像素。但是,它们最终显示(肉眼识别)的大小是一样的。
盗用一张图:
那这和一开始的问题:对于dpr=2的手机,为什么画布大小×2,就可以解决高清问题? 有什么关系? 再盗用一张图~
这样子就非常清晰了,对于dpr=2的高清(retina)屏幕而言,1个位图像素对应于4个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊(注意上述的几个颜色值)。
因为这样,我们干脆就使用2倍大的图片,比如:200*300的图片,在高清屏上,就使用400*600的图片,但是显示的大小仍然设置为200*300,这样子的话,一个物理像素仍然是显示一个位图像素,完美。
问题2:类似于下面的布局,在使用最少标签的情况下,如何适配所有移动端屏幕?并且保存一定的宽高比,不失真。
1. 使用百分比 % 进行布局
假如你使用%对该块进行布局,很快,你就会觉得非常的‘苦逼’,由于每一块有margin值,而margin的百分比你很难去计算得到,并且,你可能需要用到各种不同的媒体查询
@media(max-width:320px){
div{margin-right: 1%;}
}
每一个屏幕你都可能需要微调,并且,当以后需要修改样式的时候,你又需要每个@media都重新走一遍,想想就后怕
2.使用 rem 布局
正所谓,原理大家都懂,rem是根据根元素html的font-size的大小,直接看例子吧~
1 html{font-size: 16px;}
2 body{
3 font-size: 0.5rem; /*16px * 0.5 = 8px;*/
4 }
基于rem的原理,我们要做的就是: 针对不同手机屏幕尺寸和dpr动态的改变根节点html的font-size大小(基准值)。
回到题目上,那怎么使用rem去实现该布局?并且,在设置rem的过程都非常愉快,不需要经过各种恼人的计算?
根据当前viewport的尺寸来改变html的字体大小,一般分为媒体查询和js动态计算两种。
前者必须针对市面上较为主流的分辨率作媒体查询的区间分界点,缺点是只有设置为分界点的尺寸完美缩放,分界点之间的尺寸会出现断层。
js根据屏幕尺寸去动态计算则能令所有尺寸完美缩放,缺点则是要在页面头部插入一小段js代码。
根据这2个的优缺点,在日常开发中,h5运营页面经常使用js动态计算的方法,而平常的页面(购物,网页)则使用媒体查询的方法。
一般在开发中,我们会选择一个屏幕作为基准(比如 iphone6 的 375) ,在该屏幕下,font-size基准设置为20px
先上一个非常巧妙,极大地提高了开发效率但是原理却很简单的方法。
$rem_grid: 20 !default; //使用 rem(10)即在375屏幕中代表10px
@function rem($val){
$rem : $val / $rem_grid; //由于rem是先乘以font-size得出px,所以这里先把rem()传进来的数除以20,最后乘以html的20,其实就是参数的本来值
@if $rem == 0 {
@return #{$rem} ;
}@else{
@return $rem / ($rem * 0 + 1) * 1rem; // 带不带px单位都支持
}
}
好了,方便计算的做了,接下来是媒体查询,覆盖常见机型?
原理:根据设备的宽度(与基准设备宽度375的比值)进行缩放。废话不多说,直接上代码
@mixin query( $limit ) {
@media screen and ( min-width: $limit) { //媒体查询
& {
@content;
}
}
}
@mixin _mod_cross( $width, 375 ) {
@include query( $width * 1px ) {
font-size: $width / 375 * 20 * 1px; //这里是关键
}
}
@include _mod_cross(320, 375); //iPhone5
按照上面实现之后,当然,你要@include _mod_cross 常见的机型,这样之后,你就终于终于可以,只在iphone6下,愉快地用rem()进行布局了,无须每一个屏幕都去做适配,也无须每一次都多一步计算,大大提高开发效率。
写完媒体查询的方法,再上一份用js动态计算的代码:
原理是一样的,只是使用 document.documentElement.clientWidth 获取更加精确的设备宽度,再对html的font-size进行设置。
<script type="text/javascript">
!function(){
var maxWidth=750;
document.write(\'<style id="o2HtmlFontSize"></style>\');
var o2_resize=function(){
var cw,ch;
if(document&&document.documentElement){
cw=document.documentElement.clientWidth,ch=document.documentElement.clientHeight;
}
if(!cw||!ch){
if(window.localStorage["o2-cw"]&&window.localStorage["o2-ch"]){
cw=parseInt(window.localStorage["o2-cw"]),ch=parseInt(window.localStorage["o2-ch"]);
}else{
chk_cw();//定时检查
return ;//出错了
}
}
var zoom=maxWidth&&maxWidth<cw?maxWidth/375:cw/375,zoomY=ch/603;//由ip6 weChat
window.localStorage["o2-cw"]=cw,window.localStorage["o2-ch"]=ch;
//zoom=Math.min(zoom,zoomY);//保证ip6 wechat的显示比率
window.zoom=window.o2Zoom=zoom;
document.getElementById("o2HtmlFontSize").innerHTML=\'html{font-size:\'+(zoom*20)+\'px;}.o2-zoom,.zoom{zoom:\'+(zoom/2)+\';}.o2-scale{-webkit-transform: scale(\'+zoom/2+\'); transform: scale(\'+zoom/2+\');} .sq_sns_pic_item,.sq_sns_picmod_erea_img{-webkit-transform-origin: 0 0;transform-origin: 0 0;-webkit-transform: scale(\'+zoom/2+\');transform: scale(\'+zoom/2+\');}\';
},
siv,
chk_cw=function(){
if(siv)return ;//已经存在
siv=setInterval(function(){
//定时检查
document&&document.documentElement&&document.documentElement.clientWidth&&document.documentElement.clientHeight&&(o2_resize(),clearInterval(siv),siv=undefined);
},100);
};
o2_resize();//立即初始化
window.addEventListener("resize",o2_resize);
}();
</script>
rem的应用场景:
由于 rem 布局是相对于视口宽度,因此任何需要根据屏幕大小进行变化的元素(width、height、position 等)都可以用 rem 单位。
但 rem 也有它的缺点——不精细,其实这涉及到了浏览器渲染引擎的处理。因此,对于需要精细处理的地方(如通过 CSS 实现的 icon),可以用 px 等绝对单位,然后再通过 transform: scale() 方法等比缩放。