對於移動端開發而言,為了做到頁面高清的效果,視覺稿的規范往往會遵循以下兩點:
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() 方法等比縮放。