移動端高清適配方案(解決圖片模糊問題、1px細線問題)
本文介紹了移動端適配的3種方法,以及移動端圖片模糊問題和1px細線問題的解決方法。當(dāng)然了,在這之前先整理了與這些方法相關(guān)的知識:物理像素、設(shè)備獨立像素、設(shè)備像素比和viewport。
>>>>物理像素、設(shè)備獨立像素和設(shè)備像素比
在CSS中我們一般使用px作為單位,需要注意的是,CSS樣式里面的px和物理像素并不是相等的。CSS中的像素只是一個抽象的單位,在不同的設(shè)備或不同的環(huán)境中,CSS中的1px所代表的物理像素是不同的。在PC端,CSS的1px一般對應(yīng)著電腦屏幕的1個物理像素,但在移動端,CSS的1px等于幾個物理像素是和屏幕像素密度有關(guān)的。
物理像素(physical pixel)
物理像素又被稱為設(shè)備像素、設(shè)備物理像素,它是顯示器(電腦、手機屏幕)最小的物理顯示單位,每個物理像素由顏色值和亮度值組成。所謂的一倍屏、二倍屏(Retina)、三倍屏,指的是設(shè)備以多少物理像素來顯示一個CSS像素,也就是說,多倍屏以更多更精細的物理像素點來顯示一個CSS像素點,在普通屏幕下1個CSS像素對應(yīng)1個物理像素,而在Retina屏幕下,1個CSS像素對應(yīng)的卻是4個物理像素(參照下文田字示意圖理解)。
設(shè)備獨立像素(device-independent pixel)
設(shè)備獨立像素又被稱為CSS像素,是我們寫CSS時所用的像素,它是一個抽像的單位,主要使用在瀏覽器上,用來精確度量Web頁面上的內(nèi)容。
設(shè)備像素比(device pixel ratio)
設(shè)備像素比簡稱為dpr,定義了物理像素和設(shè)備獨立像素的對應(yīng)關(guān)系:設(shè)備像素比 = 物理像素 / 設(shè)備獨立像素。
CSS的1px等于幾個物理像素,除了和屏幕像素密度dpr有關(guān),還和用戶縮放有關(guān)系。例如,當(dāng)用戶把頁面放大一倍,那么CSS中1px所代表的物理像素也會增加一倍;反之把頁面縮小一倍,CSS中1px所代表的物理像素也會減少一倍。關(guān)于這點,在文章后面的1px細線問題部分還會講到。
>>>>viewport
viewport就是設(shè)備上用來顯示網(wǎng)頁的那一塊區(qū)域,但viewport又不局限于瀏覽器可視區(qū)域的大小,它可能比瀏覽器的可視區(qū)域要大,也可能比瀏覽器的可視區(qū)域要小。在默認情況下,一般來講,移動設(shè)備上的viewport都是要大于瀏覽器可視區(qū)域的,這是因為考慮到移動設(shè)備的分辨率相對于桌面電腦來說都比較小,所以為了能在移動設(shè)備上正常顯示那些傳統(tǒng)的為桌面瀏覽器設(shè)計的網(wǎng)站,移動設(shè)備上的瀏覽器都會把自己默認的viewport設(shè)為980px或1024px(也可能是其它值,這個是由設(shè)備自己決定的),但帶來的后果就是瀏覽器會出現(xiàn)橫向滾動條,因為瀏覽器可視區(qū)域的寬度是比這個默認的viewport的寬度要小的。
明確三種不同的viewport視口:
visual viewport 可見視口,指屏幕寬度
layout viewport 布局視口,指DOM寬度
ideal viewport 理想適口,使布局視口就是可見視口即為理想適口
獲取屏幕寬度(visual viewport)的尺寸:
window. innerWidth/Height
獲取DOM寬度(layout viewport)的尺寸:
document. documentElement. clientWidth/Height
設(shè)置理想視口ideal viewport:
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
該meta標簽的作用是讓layout viewport的寬度等于visual viewport的寬度,同時不允許用戶手動縮放,從而達到理想視口。
meta[name="viewport"]里各參數(shù)的含義為:
width: 設(shè)置layout viewport 的寬度,為一個正整數(shù),或字符串”width-device”。
initial-scale: 設(shè)置頁面的初始縮放值,為一個數(shù)字,可以帶小數(shù)。
minimum-scale: 允許用戶的最小縮放值,為一個數(shù)字,可以帶小數(shù)。
maximum-scale: 允許用戶的最大縮放值,為一個數(shù)字,可以帶小數(shù)。
height: 設(shè)置layout viewport 的高度,這個屬性對我們并不重要,很少使用。
user-scalable: 是否允許用戶進行縮放,值為“no”或“yes”。
>>>>rem適配方案
適配是為了使頁面在不同手機設(shè)備上,相對保持統(tǒng)一的效果。移動端自適應(yīng)方案很多,有百分比布局,彈性盒模型布局等,但是最好用的要數(shù)rem布局了。
rem是相對于根元素的字體大小的單位,我們可以根據(jù)設(shè)備寬度動態(tài)設(shè)置根元素的font-size,使得以rem為單位的元素在不同終端上以相對一致的視覺效果呈現(xiàn)。下面介紹3種根據(jù)屏幕寬度設(shè)置rem基準值的方法。(注:為了換算方便,以下三種方法都用1:100的比例,即1rem=100px。)
用JS設(shè)置rem基準值
/* 設(shè)計稿是750,采用1:100的比例,用1rem表示100px,font-size為100 * (clientWidth / 750) */ (function(doc, win) { var docEl = doc.documentElement, resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize', recalc = function() { var clientWidth = docEl.clientWidth; if (!clientWidth) return; docEl.style.fontSize = 100 * (clientWidth / 750) + 'px'; }; if (!doc.addEventListener) return; win.addEventListener(resizeEvt, recalc, false); doc.addEventListener('DOMContentLoaded', recalc, false); })(document, window);
用密集的媒體查詢設(shè)置font-size
/* 設(shè)計稿是750,采用1:100的比例,用1rem表示100px,100*(100/750)=13.333 以min-width: 750px時font-size: 100px為基準,
min-width每縮小100px,font-size就縮小13.3333px,如需更密集的媒體查詢可以按照這個對照關(guān)系設(shè)置。*/
@media screen and (min-width: 320px) {
html {
font-size: 42.6667px;
}
}
@media screen and (min-width: 375px) {
html {
font-size: 50px;
}
}
@media screen and (min-width: 425px) {
html {
font-size: 56.6667px;
}
}
@media screen and (min-width: 768px) {
html {
font-size: 102.4px;
}
}
用單位vw設(shè)置font-size
1vw等于屏幕可視區(qū)寬度(的可視區(qū)域的百分之一。
/* 設(shè)計稿是750,采用1:100的比例,用1rem表示100px,font-size為100*(100vw/750) */
html {
font-size: 13.3334vw;
}
注:兼容性不是很好。
了解了物理像素、設(shè)備獨立像素、設(shè)備像素比和viewport這幾個重要概念后,來看一下移動端開發(fā)中,由于屏幕分辨率導(dǎo)致的兩個經(jīng)典問題:圖片模糊問題和1px細線問題。(注:為了敘述簡潔,以下多倍屏均只敘述2倍Retina屏,其它屏幕同理。)
>>>>圖片模糊問題
一個位圖像素是柵格圖像(如:png, jpg, gif等)最小的數(shù)據(jù)單元。每一個位圖像素都包含著一些自身的顯示信息(如:顯示位置,顏色值,透明度等)。理論上,1個位圖像素對應(yīng)于1個物理像素,圖片才能得到完美清晰的展示。對于dpr=2的Retina屏幕而言,1個位圖像素對應(yīng)于4個物理像素,由于單個位圖像素不可以再進一步分割,所以只能就近取色,導(dǎo)致圖片看起來比較模糊,如下圖。
對于圖片模糊問題,比較好的方案就是用多倍圖片(@2x)。如:一個200×300(CSS pixel)的img標簽,對于dpr=2的屏幕,用400×600的圖片,如此一來,位圖像素點個數(shù)就是原來的4倍,在Retina屏幕下,位圖像素點個數(shù)就可以跟物理像素點個數(shù)形成 1 : 1的比例,圖片自然就清晰了。
如果普通屏幕下,也用了兩倍圖片,會怎樣呢?
在普通屏幕下,200×300(CSS pixel)img標簽,所對應(yīng)的物理像素個數(shù)就是200×300個,而兩倍圖片的位圖像素個數(shù)是200×300×4個,所以就出現(xiàn)一個物理像素點對應(yīng)4個位圖像素點,但它的取色也只能通過一定的算法取某一個位圖像素點上的色值,這個過程叫做(downsampling),肉眼看上去雖然圖片不會模糊,但是會覺得圖片缺少一些銳利度,或者是有點色差,如下圖。
所以最好的解決辦法是:不同的dpr下,加載不同的尺寸的圖片。不管是通過CSS媒體查詢,還是通過JS條件判斷都是可以的。
>>>>1px細線問題
在上文我們已經(jīng)知道,CSS像素為1px寬的直線,對應(yīng)的物理像素是不同的,可能是2px或者3px,而設(shè)計師想要的1px寬的直線,其實就是1物理像素寬,如下圖。
對于CSS而言,可以認為是border: 0.5px;,這是多倍屏下能顯示的最小單位。然而,并不是所有手機瀏覽器都能識別border: 0.5px,有的系統(tǒng)里,0.5px會被當(dāng)成為0px處理,那么如何實現(xiàn)這0.5px呢?網(wǎng)上有很多解決方法,比如border-image 圖片、background-image 漸變、box-shadow 等,因為這些方案不太好,所以不做贅述了,我推薦兩種方法:用媒體查詢根據(jù)dpr用“偽元素+transform”對邊框進行縮放;用JS根據(jù)屏幕尺寸和dpr精確地設(shè)置不同屏幕所應(yīng)有的rem基準值和initial-scale縮放值。
偽元素+transform
構(gòu)建1個偽元素, border為1px, 再以transform縮放到50%。
/* 設(shè)計稿是750,采用1:100的比例,font-size為100*(100vw/750) */
.border-1px {
position: relative;
}
@media screen and (-webkit-min-device-pixel-ratio: 2) {
.border-1px:before {
content: " ";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 1px;
border-top: 1px solid #D9D9D9;
color: #D9D9D9;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
}
用JS計算rem基準值和viewport縮放值
/* 設(shè)計稿是750,采用1:100的比例,font-size為100 * (docEl.clientWidth * dpr / 750) */ 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 = 100 * (docEl.clientWidth * dpr / 750); scale = 1 / dpr; // 設(shè)置viewport,進行縮放,達到高清效果 metaEl.setAttribute('content', 'width=' + dpr * docEl.clientWidth + ',initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no'); // 設(shè)置data-dpr屬性,留作的css hack之用,解決圖片模糊問題和1px細線問題 docEl.setAttribute('data-dpr', dpr); // 動態(tài)寫入樣式 docEl.firstElementChild.appendChild(fontEl); fontEl.innerHTML = 'html{font-size:' + rem + 'px!important;}';
相較與于上文rem適配方案里“用JS計算rem基準值”的方案,這個“用JS計算rem基準值和viewport縮放值”的方案可以解決1px細線問題。表格以2倍Retina屏做比較,其他多倍屏同理。
用JS根據(jù)屏幕尺寸和dpr精確地設(shè)置不同屏幕所應(yīng)有的rem基準值和initial-scale縮放值,這個JS方案已經(jīng)在完美解決了1px細線問題,我們不需要再做任何事情,至于圖片模糊問題,只需要根據(jù)data-dpr的值動態(tài)加載不同尺寸的圖就可以了。
分享一個公眾號-----前端麻辣燙 ,一個專注于前端技術(shù)學(xué)習(xí)與交流的公眾號~
微信搜索“WebSnacks”,或者掃描下方二維碼。

浙公網(wǎng)安備 33010602011771號