關(guān)于移動(dòng)端適配,你必須要知道的

移動(dòng)端適配,是我們?cè)陂_(kāi)發(fā)中經(jīng)常會(huì)遇到的,這里面可能會(huì)遇到非常多的問(wèn)題:

  • 1px問(wèn)題

  • UI圖完美適配方案

  • iPhoneX適配方案

  • 橫屏適配

  • 高清屏圖片模糊問(wèn)題

  • ...

上面這些問(wèn)題可能我們?cè)陂_(kāi)發(fā)中已經(jīng)知道如何解決,但是問(wèn)題產(chǎn)生的原理,以及解決方案的原理可能會(huì)模糊不清。在解決這些問(wèn)題的過(guò)程中,我們往往會(huì)遇到非常多的概念:像素、分辨率、 PPIDPIDPDIPDPR、視口等等,你真的能分清這些概念的意義嗎?

本文將從移動(dòng)端適配的基礎(chǔ)概念出發(fā),探究移動(dòng)端適配各種問(wèn)題的解決方案和實(shí)現(xiàn)原理。

一、英寸

一般用英寸描述屏幕的物理大小,如電腦顯示器的 1722,手機(jī)顯示器的 4.85.7等使用的單位都是英寸。

需要注意,上面的尺寸都是屏幕對(duì)角線的長(zhǎng)度:

image

英寸( inch,縮寫(xiě)為 in)在荷蘭語(yǔ)中的本意是大拇指,一英寸就是指甲底部普通人拇指的寬度。

英寸和厘米的換算: 1英寸=2.54厘米

二、分辨率

2.1 像素

像素即一個(gè)小方塊,它具有特定的位置和顏色。

圖片、電子屏幕(手機(jī)、電腦)就是由無(wú)數(shù)個(gè)具有特定顏色和特定位置的小方塊拼接而成。

像素可以作為圖片或電子屏幕的最小組成單位。

下面我們使用 sketch打開(kāi)一張圖片:

image

將這些圖片放大即可看到這些像素點(diǎn):

image

通常我們所說(shuō)的分辨率有兩種,屏幕分辨率和圖像分辨率。

2.2 屏幕分辨率

屏幕分辨率指一個(gè)屏幕具體由多少個(gè)像素點(diǎn)組成。

下面是 apple的官網(wǎng)上對(duì)手機(jī)分辨率的描述:

image

iPhone XSMaxiPhone SE的分辨率分別為 2688x12421136x640。這表示手機(jī)分別在垂直和水平上所具有的像素點(diǎn)數(shù)。

當(dāng)然分辨率高不代表屏幕就清晰,屏幕的清晰程度還與尺寸有關(guān)。

2.3 圖像分辨率

我們通常說(shuō)的 圖片分辨率其實(shí)是指圖片含有的 像素?cái)?shù),比如一張圖片的分辨率為 800x400。這表示圖片分別在垂直和水平上所具有的像素點(diǎn)數(shù)為 800400

同一尺寸的圖片,分辨率越高,圖片越清晰。

image

2.4 PPI

PPI(PixelPerInch):每英寸包括的像素?cái)?shù)。

PPI可以用于描述屏幕的清晰度以及一張圖片的質(zhì)量。

使用 PPI描述圖片時(shí), PPI越高,圖片質(zhì)量越高,使用 PPI描述屏幕時(shí), PPI越高,屏幕越清晰。

在上面描述手機(jī)分辨率的圖片中,我們可以看到: iPhone XSMaxiPhone SEPPI分別為 458326,這足以證明前者的屏幕更清晰。

由于手機(jī)尺寸為手機(jī)對(duì)角線的長(zhǎng)度,我們通常使用如下的方法計(jì)算 PPI:

\frac{\sqrt{水平像素點(diǎn)數(shù)^2+垂直像素點(diǎn)數(shù)^2}}{尺寸}

iPhone6PPI\frac{\sqrt{1334^2+750^2}}{4.7}=325.6,那它每英寸約含有 326個(gè)物理像素點(diǎn)。

2.5 DPI

DPI(DotPerInch):即每英寸包括的點(diǎn)數(shù)。

這里的點(diǎn)是一個(gè)抽象的單位,它可以是屏幕像素點(diǎn)、圖片像素點(diǎn)也可以是打印機(jī)的墨點(diǎn)。

平時(shí)你可能會(huì)看到使用 DPI來(lái)描述圖片和屏幕,這時(shí)的 DPI應(yīng)該和 PPI是等價(jià)的, DPI最常用的是用于描述打印機(jī),表示打印機(jī)每英寸可以打印的點(diǎn)數(shù)。

一張圖片在屏幕上顯示時(shí),它的像素點(diǎn)數(shù)是規(guī)則排列的,每個(gè)像素點(diǎn)都有特定的位置和顏色。

當(dāng)使用打印機(jī)進(jìn)行打印時(shí),打印機(jī)可能不會(huì)規(guī)則的將這些點(diǎn)打印出來(lái),而是使用一個(gè)個(gè)打印點(diǎn)來(lái)呈現(xiàn)這張圖像,這些打印點(diǎn)之間會(huì)有一定的空隙,這就是 DPI所描述的:打印點(diǎn)的密度。

image

在上面的圖像中我們可以清晰的看到,打印機(jī)是如何使用墨點(diǎn)來(lái)打印一張圖像。

所以,打印機(jī)的 DPI越高,打印圖像的精細(xì)程度就越高,同時(shí)這也會(huì)消耗更多的墨點(diǎn)和時(shí)間。

三、設(shè)備獨(dú)立像素

實(shí)際上,上面我們描述的像素都是 物理像素,即設(shè)備上真實(shí)的物理單元。

下面我們來(lái)看看 設(shè)備獨(dú)立像素究竟是如何產(chǎn)生的:

智能手機(jī)發(fā)展非常之快,在幾年之前,我們還用著分辨率非常低的手機(jī),比如下面左側(cè)的白色手機(jī),它的分辨率是 320x480,我們可以在上面瀏覽正常的文字、圖片等等。

但是,隨著科技的發(fā)展,低分辨率的手機(jī)已經(jīng)不能滿足我們的需求了。很快,更高分辨率的屏幕誕生了,比如下面的黑色手機(jī),它的分辨率是 640x940,正好是白色手機(jī)的兩倍。

理論上來(lái)講,在白色手機(jī)上相同大小的圖片和文字,在黑色手機(jī)上會(huì)被縮放一倍,因?yàn)樗姆直媛侍岣吡艘槐丁_@樣,豈不是后面出現(xiàn)更高分辨率的手機(jī),頁(yè)面元素會(huì)變得越來(lái)越小嗎?

image

然而,事實(shí)并不是這樣的,我們現(xiàn)在使用的智能手機(jī),不管分辨率多高,他們所展示的界面比例都是基本類似的。喬布斯在 iPhone4的發(fā)布會(huì)上首次提出了 RetinaDisplay(視網(wǎng)膜屏幕)的概念,它正是解決了上面的問(wèn)題,這也使它成為一款跨時(shí)代的手機(jī)。

image

iPhone4使用的視網(wǎng)膜屏幕中,把 2x2個(gè)像素當(dāng) 1個(gè)像素使用,這樣讓屏幕看起來(lái)更精致,但是元素的大小卻不會(huì)改變。

image

如果黑色手機(jī)使用了視網(wǎng)膜屏幕的技術(shù),那么顯示結(jié)果應(yīng)該是下面的情況,比如列表的寬度為 300個(gè)像素,那么在一條水平線上,白色手機(jī)會(huì)用 300個(gè)物理像素去渲染它,而黑色手機(jī)實(shí)際上會(huì)用 600個(gè)物理像素去渲染它。

我們必須用一種單位來(lái)同時(shí)告訴不同分辨率的手機(jī),它們?cè)诮缑嫔巷@示元素的大小是多少,這個(gè)單位就是設(shè)備獨(dú)立像素( DeviceIndependentPixels)簡(jiǎn)稱 DIPDP。上面我們說(shuō),列表的寬度為 300個(gè)像素,實(shí)際上我們可以說(shuō):列表的寬度為300個(gè)設(shè)備獨(dú)立像素。

image

打開(kāi) chrome的開(kāi)發(fā)者工具,我們可以模擬各個(gè)手機(jī)型號(hào)的顯示情況,每種型號(hào)上面會(huì)顯示一個(gè)尺寸,比如 iPhone X顯示的尺寸是 375x812,實(shí)際 iPhone X的分辨率會(huì)比這高很多,這里顯示的就是設(shè)備獨(dú)立像素。

image

3.1 設(shè)備像素比

設(shè)備像素比 device pixel ratio簡(jiǎn)稱 dpr,即物理像素和設(shè)備獨(dú)立像素的比值。

web中,瀏覽器為我們提供了 window.devicePixelRatio來(lái)幫助我們獲取 dpr

css中,可以使用媒體查詢 min-device-pixel-ratio,區(qū)分 dpr

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. @media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2){ }

</pre>

ReactNative中,我們也可以使用 PixelRatio.get()來(lái)獲取 DPR

當(dāng)然,上面的規(guī)則也有例外, iPhone6、7、8Plus的實(shí)際物理像素是 1080x1920,在開(kāi)發(fā)者工具中我們可以看到:它的設(shè)備獨(dú)立像素是 414x736,設(shè)備像素比為 3,設(shè)備獨(dú)立像素和設(shè)備像素比的乘積并不等于 1080x1920,而是等于 1242x2208

實(shí)際上,手機(jī)會(huì)自動(dòng)把 1242x2208個(gè)像素點(diǎn)塞進(jìn) 1080*1920個(gè)物理像素點(diǎn)來(lái)渲染,我們不用關(guān)心這個(gè)過(guò)程,而 1242x2208被稱為屏幕的 設(shè)計(jì)像素。我們開(kāi)發(fā)過(guò)程中也是以這個(gè) 設(shè)計(jì)像素為準(zhǔn)。

實(shí)際上,從蘋(píng)果提出視網(wǎng)膜屏幕開(kāi)始,才出現(xiàn)設(shè)備像素比這個(gè)概念,因?yàn)樵谶@之前,移動(dòng)設(shè)備都是直接使用物理像素來(lái)進(jìn)行展示。

緊接著, Android同樣使用了其他的技術(shù)方案來(lái)實(shí)現(xiàn) DPR大于 1的屏幕,不過(guò)原理是類似的。由于 Android屏幕尺寸非常多、分辨率高低跨度非常大,不像蘋(píng)果只有它自己的幾款固定設(shè)備、尺寸。所以,為了保證各種設(shè)備的顯示效果, Android按照設(shè)備的像素密度將設(shè)備分成了幾個(gè)區(qū)間:

image

當(dāng)然,所有的 Android設(shè)備不一定嚴(yán)格按照上面的分辨率,每個(gè)類型可能對(duì)應(yīng)幾種不同分辨率,所以,每個(gè) Android手機(jī)都能根據(jù)給定的區(qū)間范圍,確定自己的 DPR,從而擁有類似的顯示。當(dāng)然,僅僅是類似,由于各個(gè)設(shè)備的尺寸、分辨率上的差異,設(shè)備獨(dú)立像素也不會(huì)完全相等,所以各種 Android設(shè)備仍然不能做到在展示上完全相等。

3.2 移動(dòng)端開(kāi)發(fā)

iOSAndroidReactNative開(kāi)發(fā)中樣式單位其實(shí)都使用的是設(shè)備獨(dú)立像素。

iOS的尺寸單位為 ptAndroid的尺寸單位為 dpReactNative中沒(méi)有指定明確的單位,它們其實(shí)都是設(shè)備獨(dú)立像素 dp

在使用 ReactNative開(kāi)發(fā) App時(shí), UI給我們的原型圖一般是基于 iphone6的像素給定的。

為了適配所有機(jī)型,我們?cè)趯?xiě)樣式時(shí)需要把物理像素轉(zhuǎn)換為設(shè)備獨(dú)立像素:例如:如果給定一個(gè)元素的高度為 200px(這里的 px指物理像素,非 CSS像素), iphone6的設(shè)備像素比為 2,我們給定的 height應(yīng)為 200px/2=100dp

當(dāng)然,最好的是,你可以和設(shè)計(jì)溝通好,所有的 UI圖都按照設(shè)備獨(dú)立像素來(lái)出。

我們還可以在代碼( ReactNative)中進(jìn)行 pxdp的轉(zhuǎn)換:

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. import {PixelRatio } from 'react-native';

  2. const dpr = PixelRatio.get();

  3. /**

  4. * px轉(zhuǎn)換為dp

  5. */

  6. export function pxConvertTodp(px) {

  7. return px / dpr;

  8. }

  9. /**

  10. * dp轉(zhuǎn)換為px

  11. */

  12. export function dpConvertTopx(dp) {

  13. return PixelRatio.getPixelSizeForLayoutSize(dp);

  14. }

</pre>

3.3 WEB端開(kāi)發(fā)

在寫(xiě) CSS時(shí),我們用到最多的單位是 px,即 CSS像素,當(dāng)頁(yè)面縮放比例為 100%時(shí),一個(gè) CSS像素等于一個(gè)設(shè)備獨(dú)立像素。

但是 CSS像素是很容易被改變的,當(dāng)用戶對(duì)瀏覽器進(jìn)行了放大, CSS像素會(huì)被放大,這時(shí)一個(gè) CSS像素會(huì)跨越更多的物理像素。

頁(yè)面的縮放系數(shù)=CSS像素/設(shè)備獨(dú)立像素

3.4 關(guān)于屏幕

這里多說(shuō)兩句 Retina屏幕,因?yàn)槲以诤芏辔恼轮锌吹綄?duì) Retina屏幕的誤解。

Retina屏幕只是蘋(píng)果提出的一個(gè)營(yíng)銷術(shù)語(yǔ):

在普通的使用距離下,人的肉眼無(wú)法分辨單個(gè)的像素點(diǎn)。

為什么強(qiáng)調(diào) 普通的使用距離下呢?我們來(lái)看一下它的計(jì)算公式:

a=2arctan(h/2d)

a代表人眼視角, h代表像素間距, d代表肉眼與屏幕的距離,符合以上條件的屏幕可以使肉眼看不見(jiàn)單個(gè)物理像素點(diǎn)。

它不能單純的表達(dá)分辨率和 PPI,只能一種表達(dá)視覺(jué)效果。

讓多個(gè)物理像素渲染一個(gè)獨(dú)立像素只是 Retina屏幕為了達(dá)到效果而使用的一種技術(shù)。而不是所有 DPR>1的屏幕就是 Retina屏幕。

比如:給你一塊超大尺寸的屏幕,即使它的 PPI很高, DPR也很高,在近距離你也能看清它的像素點(diǎn),這就不算 Retina屏幕。

image

我們經(jīng)常見(jiàn)到用 KP這個(gè)單位來(lái)形容屏幕:

P代表的就是屏幕縱向的像素個(gè)數(shù), 1080P即縱向有 1080個(gè)像素,分辨率為 1920X1080的屏幕就屬于 1080P屏幕。

我們平時(shí)所說(shuō)的高清屏其實(shí)就是屏幕的物理分辨率達(dá)到或超過(guò) 1920X1080的屏幕。

K代表屏幕橫向有幾個(gè) 1024個(gè)像素,一般來(lái)講橫向像素超過(guò) 2048就屬于 2K屏,橫向像素超過(guò) 4096就屬于 4K屏。

四、視口

視口( viewport)代表當(dāng)前可見(jiàn)的計(jì)算機(jī)圖形區(qū)域。在 Web瀏覽器術(shù)語(yǔ)中,通常與瀏覽器窗口相同,但不包括瀏覽器的 UI, 菜單欄等——即指你正在瀏覽的文檔的那一部分。

一般我們所說(shuō)的視口共包括三種:布局視口、視覺(jué)視口和理想視口,它們?cè)谄聊贿m配中起著非常重要的作用。

4.1 布局視口

image

布局視口( layout viewport):當(dāng)我們以百分比來(lái)指定一個(gè)元素的大小時(shí),它的計(jì)算值是由這個(gè)元素的包含塊計(jì)算而來(lái)的。當(dāng)這個(gè)元素是最頂級(jí)的元素時(shí),它就是基于布局視口來(lái)計(jì)算的。

所以,布局視口是網(wǎng)頁(yè)布局的基準(zhǔn)窗口,在 PC瀏覽器上,布局視口就等于當(dāng)前瀏覽器的窗口大小(不包括 bordersmargins、滾動(dòng)條)。

在移動(dòng)端,布局視口被賦予一個(gè)默認(rèn)值,大部分為 980px,這保證 PC的網(wǎng)頁(yè)可以在手機(jī)瀏覽器上呈現(xiàn),但是非常小,用戶可以手動(dòng)對(duì)網(wǎng)頁(yè)進(jìn)行放大。

我們可以通過(guò)調(diào)用 document.documentElement.clientWidth/clientHeight來(lái)獲取布局視口大小。

4.2 視覺(jué)視口

image

視覺(jué)視口( visual viewport):用戶通過(guò)屏幕真實(shí)看到的區(qū)域。

視覺(jué)視口默認(rèn)等于當(dāng)前瀏覽器的窗口大小(包括滾動(dòng)條寬度)。

當(dāng)用戶對(duì)瀏覽器進(jìn)行縮放時(shí),不會(huì)改變布局視口的大小,所以頁(yè)面布局是不變的,但是縮放會(huì)改變視覺(jué)視口的大小。

例如:用戶將瀏覽器窗口放大了 200%,這時(shí)瀏覽器窗口中的 CSS像素會(huì)隨著視覺(jué)視口的放大而放大,這時(shí)一個(gè) CSS像素會(huì)跨越更多的物理像素。

所以,布局視口會(huì)限制你的 CSS布局而視覺(jué)視口決定用戶具體能看到什么。

我們可以通過(guò)調(diào)用 window.innerWidth/innerHeight來(lái)獲取視覺(jué)視口大小。

4.3 理想視口

image

布局視口在移動(dòng)端展示的效果并不是一個(gè)理想的效果,所以理想視口( ideal viewport)就誕生了:網(wǎng)站頁(yè)面在移動(dòng)端展示的理想大小。

如上圖,我們?cè)诿枋鲈O(shè)備獨(dú)立像素時(shí)曾使用過(guò)這張圖,在瀏覽器調(diào)試移動(dòng)端時(shí)頁(yè)面上給定的像素大小就是理想視口大小,它的單位正是設(shè)備獨(dú)立像素。

上面在介紹 CSS像素時(shí)曾經(jīng)提到 頁(yè)面的縮放系數(shù)=CSS像素/設(shè)備獨(dú)立像素,實(shí)際上說(shuō) 頁(yè)面的縮放系數(shù)=理想視口寬度/視覺(jué)視口寬度更為準(zhǔn)確。

所以,當(dāng)頁(yè)面縮放比例為 100%時(shí), CSS像素=設(shè)備獨(dú)立像素理想視口=視覺(jué)視口

我們可以通過(guò)調(diào)用 screen.width/height來(lái)獲取理想視口大小。

4.4 Meta viewport

<meta> 元素表示那些不能由其它 HTML元相關(guān)元素之一表示的任何元數(shù)據(jù)信息,它可以告訴瀏覽器如何解析頁(yè)面。

我們可以借助 <meta>元素的 viewport來(lái)幫助我們?cè)O(shè)置視口、縮放等,從而讓移動(dòng)端得到更好的展示效果。

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. <meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;">

</pre>

上面是 viewport的一個(gè)配置,我們來(lái)看看它們的具體含義:

Value| 可能值| 描述 -|-|- width| 正整數(shù)或 device-width | 以 pixels(像素)為單位, 定義布局視口的寬度。 height| 正整數(shù)或 device-height | 以 pixels(像素)為單位, 定義布局視口的高度。 initial-scale| 0.0-10.0|定義頁(yè)面初始縮放比率。 minimum-scale| 0.0-10.0|定義縮放的最小值;必須小于或等于 maximum-scale的值。 maximum-scale| 0.0-10.0|定義縮放的最大值;必須大于或等于 minimum-scale的值。 user-scalable| 一個(gè)布爾值( yes或者 no)| 如果設(shè)置為 no,用戶將不能放大或縮小網(wǎng)頁(yè)。默認(rèn)值為 yes。

4.5 移動(dòng)端適配

為了在移動(dòng)端讓頁(yè)面獲得更好的顯示效果,我們必須讓布局視口、視覺(jué)視口都盡可能等于理想視口。

device-width就等于理想視口的寬度,所以設(shè)置 width=device-width就相當(dāng)于讓布局視口等于理想視口。

由于 initial-scale=理想視口寬度/視覺(jué)視口寬度,所以我們?cè)O(shè)置 initial-scale=1;就相當(dāng)于讓視覺(jué)視口等于理想視口。

這時(shí),1個(gè) CSS像素就等于1個(gè)設(shè)備獨(dú)立像素,而且我們也是基于理想視口來(lái)進(jìn)行布局的,所以呈現(xiàn)出來(lái)的頁(yè)面布局在各種設(shè)備上都能大致相似。

4.6 縮放

上面提到 width可以決定布局視口的寬度,實(shí)際上它并不是布局視口的唯一決定性因素,設(shè)置 initial-scale也有肯能影響到布局視口,因?yàn)椴季忠暱趯挾热〉氖?width和視覺(jué)視口寬度的最大值。

例如:若手機(jī)的理想視口寬度為 400px,設(shè)置 width=device-widthinitial-scale=2,此時(shí) 視覺(jué)視口寬度=理想視口寬度/initial-scale200px,布局視口取兩者最大值即 device-width 400px

若設(shè)置 width=device-widthinitial-scale=0.5,此時(shí) 視覺(jué)視口寬度=理想視口寬度/initial-scale800px,布局視口取兩者最大值即 800px

4.7 獲取瀏覽器大小

瀏覽器為我們提供的獲取窗口大小的 API有很多,下面我們?cè)賮?lái)對(duì)比一下:

image
  • window.innerHeight:獲取瀏覽器視覺(jué)視口高度(包括垂直滾動(dòng)條)。

  • window.outerHeight:獲取瀏覽器窗口外部的高度。表示整個(gè)瀏覽器窗口的高度,包括側(cè)邊欄、窗口鑲邊和調(diào)正窗口大小的邊框。

  • window.screen.Height:獲取獲屏幕取理想視口高度,這個(gè)數(shù)值是固定的, 設(shè)備的分辨率/設(shè)備像素比

  • window.screen.availHeight:瀏覽器窗口可用的高度。

  • document.documentElement.clientHeight:獲取瀏覽器布局視口高度,包括內(nèi)邊距,但不包括垂直滾動(dòng)條、邊框和外邊距。

  • document.documentElement.offsetHeight:包括內(nèi)邊距、滾動(dòng)條、邊框和外邊距。

  • document.documentElement.scrollHeight:在不使用滾動(dòng)條的情況下適合視口中的所有內(nèi)容所需的最小寬度。測(cè)量方式與 clientHeight相同:它包含元素的內(nèi)邊距,但不包括邊框,外邊距或垂直滾動(dòng)條。

五、1px問(wèn)題

為了適配各種屏幕,我們寫(xiě)代碼時(shí)一般使用設(shè)備獨(dú)立像素來(lái)對(duì)頁(yè)面進(jìn)行布局。

而在設(shè)備像素比大于 1的屏幕上,我們寫(xiě)的 1px實(shí)際上是被多個(gè)物理像素渲染,這就會(huì)出現(xiàn) 1px在有些屏幕上看起來(lái)很粗的現(xiàn)象。

5.1 border-image

基于 media查詢判斷不同的設(shè)備像素比給定不同的 border-image

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. .border_1px{

  2. border-bottom: 1px solid #000;

  3. }

  4. @media only screen and (-webkit-min-device-pixel-ratio:2){

  5. .border_1px{

  6. border-bottom: none;

  7. border-width: 0 0 1px 0;

  8. border-image: url(../img/1pxline.png) 0 0 2 0 stretch;

  9. }

  10. }

</pre>

5.2 background-image

border-image類似,準(zhǔn)備一張符合條件的邊框背景圖,模擬在背景上。

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. .border_1px{

  2. border-bottom: 1px solid #000;

  3. }

  4. @media only screen and (-webkit-min-device-pixel-ratio:2){

  5. .border_1px{

  6. background: url(../img/1pxline.png) repeat-x left bottom;

  7. background-size: 100% 1px;

  8. }

  9. }

</pre>

上面兩種都需要單獨(dú)準(zhǔn)備圖片,而且圓角不是很好處理,但是可以應(yīng)對(duì)大部分場(chǎng)景。

5.3 偽類 + transform

基于 media查詢判斷不同的設(shè)備像素比對(duì)線條進(jìn)行縮放:

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. .border_1px:before{

  2. content: '';

  3. position: absolute;

  4. top: 0;

  5. height: 1px;

  6. width: 100%;

  7. background-color: #000;

  8. transform-origin: 50% 0%;

  9. }

  10. @media only screen and (-webkit-min-device-pixel-ratio:2){

  11. .border_1px:before{

  12. transform: scaleY(0.5);

  13. }

  14. }

  15. @media only screen and (-webkit-min-device-pixel-ratio:3){

  16. .border_1px:before{

  17. transform: scaleY(0.33);

  18. }

  19. }

</pre>

這種方式可以滿足各種場(chǎng)景,如果需要滿足圓角,只需要給偽類也加上 border-radius即可。

5.4 svg

上面我們 border-imagebackground-image都可以模擬 1px邊框,但是使用的都是位圖,還需要外部引入。

借助 PostCSSpostcss-write-svg我們能直接使用 border-imagebackground-image創(chuàng)建 svg1px邊框:

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. @svg border_1px {

  2. height: 2px;

  3. @rect {

  4. fill: var(--color, black);

  5. width: 100%;

  6. height: 50%;

  7. }

  8. }

  9. .example { border: 1px solid transparent; border-image: svg(border_1px param(--color #00b1ff)) 2 2 stretch; }

</pre>

編譯后:

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. .example { border: 1px solid transparent; border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") 2 2 stretch; }

</pre>

上面的方案是大漠在他的文章中推薦使用的,基本可以滿足所有場(chǎng)景,而且不需要外部引入,這是我個(gè)人比較喜歡的一種方案。

5.5 設(shè)置viewport

通過(guò)設(shè)置縮放,讓 CSS像素等于真正的物理像素。

例如:當(dāng)設(shè)備像素比為 3時(shí),我們將頁(yè)面縮放 1/3倍,這時(shí) 1px等于一個(gè)真正的屏幕像素。

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. const scale = 1 / window.devicePixelRatio;

  2. const viewport = document.querySelector('meta[name="viewport"]');

  3. if (!viewport) {

  4. viewport = document.createElement('meta');

  5. viewport.setAttribute('name', 'viewport');

  6. window.document.head.appendChild(viewport);

  7. }

  8. viewport.setAttribute('content', 'width=device-width,user-scalable=no,initial-scale=' + scale + ',maximum-scale=' + scale + ',minimum-scale=' + scale);

</pre>

實(shí)際上,上面這種方案是早先 flexible采用的方案。

當(dāng)然,這樣做是要付出代價(jià)的,這意味著你頁(yè)面上所有的布局都要按照物理像素來(lái)寫(xiě)。這顯然是不現(xiàn)實(shí)的,這時(shí),我們可以借助 flexiblevw、vh來(lái)幫助我們進(jìn)行適配。

六、移動(dòng)端適配方案

盡管我們可以使用設(shè)備獨(dú)立像素來(lái)保證各個(gè)設(shè)備在不同手機(jī)上顯示的效果類似,但這并不能保證它們顯示完全一致,我們需要一種方案來(lái)讓設(shè)計(jì)稿得到更完美的適配。

6.1 flexible方案

flexible方案是阿里早期開(kāi)源的一個(gè)移動(dòng)端適配解決方案,引用 flexible后,我們?cè)陧?yè)面上統(tǒng)一使用 rem來(lái)布局。

它的核心代碼非常簡(jiǎn)單:

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. // set 1rem = viewWidth / 10

  2. function setRemUnit () {

  3. var rem = docEl.clientWidth / 10

  4. docEl.style.fontSize = rem + 'px'

  5. }

  6. setRemUnit();

</pre>

rem 是相對(duì)于 html節(jié)點(diǎn)的 font-size來(lái)做計(jì)算的。

我們通過(guò)設(shè)置 document.documentElement.style.fontSize就可以統(tǒng)一整個(gè)頁(yè)面的布局標(biāo)準(zhǔn)。

上面的代碼中,將 html節(jié)點(diǎn)的 font-size設(shè)置為頁(yè)面 clientWidth(布局視口)的 1/10,即 1rem就等于頁(yè)面布局視口的 1/10,這就意味著我們后面使用的 rem都是按照頁(yè)面比例來(lái)計(jì)算的。

這時(shí),我們只需要將 UI出的圖轉(zhuǎn)換為 rem即可。

iPhone6為例:布局視口為 375px,則 1rem=37.5px,這時(shí) UI給定一個(gè)元素的寬為 75px(設(shè)備獨(dú)立像素),我們只需要將它設(shè)置為 75/37.5=2rem

當(dāng)然,每個(gè)布局都要計(jì)算非常繁瑣,我們可以借助 PostCSSpx2rem插件來(lái)幫助我們完成這個(gè)過(guò)程。

下面的代碼可以保證在頁(yè)面大小變化時(shí),布局可以自適應(yīng),當(dāng)觸發(fā)了 windowresizepageShow事件之后自動(dòng)調(diào)整 htmlfontSize大小。

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. // reset rem unit on page resize

  2. window.addEventListener('resize', setRemUnit)window.addEventListener('pageshow', function (e) {

  3. if (e.persisted) {

  4. setRemUnit()

  5. }

  6. })

</pre>

由于 viewport單位得到眾多瀏覽器的兼容,上面這種方案現(xiàn)在已經(jīng)被官方棄用:

lib-flexible這個(gè)過(guò)渡方案已經(jīng)可以放棄使用,不管是現(xiàn)在的版本還是以前的版本,都存有一定的問(wèn)題。建議大家開(kāi)始使用viewport來(lái)替代此方案。

下面我們來(lái)看看現(xiàn)在最流行的 vh、vw方案。

6.2 vh、vw方案

vh、vw方案即將視覺(jué)視口寬度 window.innerWidth和視覺(jué)視口高度 window.innerHeight 等分為 100 份。

上面的 flexible方案就是模仿這種方案,因?yàn)樵缧r(shí)候 vw還沒(méi)有得到很好的兼容。

  • vw(Viewport's width)1vw等于視覺(jué)視口的 1%

  • vh(Viewport's height) : 1vh 為視覺(jué)視口高度的 1%

  • vmin : vwvh 中的較小值

  • vmax : 選取 vwvh 中的較大值

image

如果視覺(jué)視口為 375px,那么 1vw=3.75px,這時(shí) UI給定一個(gè)元素的寬為 75px(設(shè)備獨(dú)立像素),我們只需要將它設(shè)置為 75/3.75=20vw

這里的比例關(guān)系我們也不用自己換算,我們可以使用 PostCSSpostcss-px-to-viewport 插件幫我們完成這個(gè)過(guò)程。寫(xiě)代碼時(shí),我們只需要根據(jù) UI給的設(shè)計(jì)圖寫(xiě) px單位即可。

當(dāng)然,沒(méi)有一種方案是十全十美的, vw同樣有一定的缺陷:

  • px轉(zhuǎn)換成 vw不一定能完全整除,因此有一定的像素差。

  • 比如當(dāng)容器使用 vwmargin采用 px時(shí),很容易造成整體寬度超過(guò) 100vw,從而影響布局效果。當(dāng)然我們也是可以避免的,例如使用 padding代替 margin,結(jié)合 calc()函數(shù)使用等等...

七、適配iPhoneX

iPhoneX的出現(xiàn)將手機(jī)的顏值帶上了一個(gè)新的高度,它取消了物理按鍵,改成了底部的小黑條,但是這樣的改動(dòng)給開(kāi)發(fā)者適配移動(dòng)端又增加了難度。

7.1 安全區(qū)域

iPhoneX發(fā)布后,許多廠商相繼推出了具有邊緣屏幕的手機(jī)。

image

這些手機(jī)和普通手機(jī)在外觀上無(wú)外乎做了三個(gè)改動(dòng):圓角( corners)、劉海( sensor housing)和小黑條( HomeIndicator)。為了適配這些手機(jī),安全區(qū)域這個(gè)概念變誕生了:安全區(qū)域就是一個(gè)不受上面三個(gè)效果的可視窗口范圍。

為了保證頁(yè)面的顯示效果,我們必須把頁(yè)面限制在安全范圍內(nèi),但是不影響整體效果。

7.2 viewport-fit

viewport-fit是專門(mén)為了適配 iPhoneX而誕生的一個(gè)屬性,它用于限制網(wǎng)頁(yè)如何在安全區(qū)域內(nèi)進(jìn)行展示。

image

contain: 可視窗口完全包含網(wǎng)頁(yè)內(nèi)容

cover:網(wǎng)頁(yè)內(nèi)容完全覆蓋可視窗口

默認(rèn)情況下或者設(shè)置為 autocontain效果相同。

7.3 env、constant

image

我們需要將頂部和底部合理的擺放在安全區(qū)域內(nèi), iOS11新增了兩個(gè) CSS函數(shù) env、constant,用于設(shè)定安全區(qū)域與邊界的距離。

函數(shù)內(nèi)部可以是四個(gè)常量:

  • safe-area-inset-left:安全區(qū)域距離左邊邊界距離

  • safe-area-inset-right:安全區(qū)域距離右邊邊界距離

  • safe-area-inset-top:安全區(qū)域距離頂部邊界距離

  • safe-area-inset-bottom:安全區(qū)域距離底部邊界距離

注意:我們必須指定 viweport-fit后才能使用這兩個(gè)函數(shù):

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. <meta name="viewport" content="viewport-fit=cover">

</pre>

constantiOS<11.2的版本中生效, enviOS>=11.2的版本中生效,這意味著我們往往要同時(shí)設(shè)置他們,將頁(yè)面限制在安全區(qū)域內(nèi):

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. body {

  2. padding-bottom: constant(safe-area-inset-bottom);

  3. padding-bottom: env(safe-area-inset-bottom);

  4. }

</pre>

當(dāng)使用底部固定導(dǎo)航欄時(shí),我們要為他們?cè)O(shè)置 padding值:

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. {

  2. padding-bottom: constant(safe-area-inset-bottom);

  3. padding-bottom: env(safe-area-inset-bottom);

  4. }

</pre>

八、橫屏適配

image

很多視口我們要對(duì)橫屏和豎屏顯示不同的布局,所以我們需要檢測(cè)在不同的場(chǎng)景下給定不同的樣式:

8.1 JavaScript檢測(cè)橫屏

window.orientation:獲取屏幕旋轉(zhuǎn)方向

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. window.addEventListener("resize", ()=>{

  2. if (window.orientation === 180 || window.orientation === 0) {

  3. // 正常方向或屏幕旋轉(zhuǎn)180度

  4. console.log('豎屏');

  5. };

  6. if (window.orientation === 90 || window.orientation === -90 ){

  7. // 屏幕順時(shí)鐘旋轉(zhuǎn)90度或屏幕逆時(shí)針旋轉(zhuǎn)90度

  8. console.log('橫屏');

  9. }

  10. });

</pre>

8.2 CSS檢測(cè)橫屏

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. @media screen and (orientation: portrait) {

  2. /*豎屏...*/

  3. }

  4. @media screen and (orientation: landscape) {

  5. /*橫屏...*/

  6. }

</pre>

九、圖片模糊問(wèn)題

9.1 產(chǎn)生原因

我們平時(shí)使用的圖片大多數(shù)都屬于位圖( png、jpg...),位圖由一個(gè)個(gè)像素點(diǎn)構(gòu)成的,每個(gè)像素都具有特定的位置和顏色值:

image

理論上,位圖的每個(gè)像素對(duì)應(yīng)在屏幕上使用一個(gè)物理像素來(lái)渲染,才能達(dá)到最佳的顯示效果。

而在 dpr>1的屏幕上,位圖的一個(gè)像素可能由多個(gè)物理像素來(lái)渲染,然而這些物理像素點(diǎn)并不能被準(zhǔn)確的分配上對(duì)應(yīng)位圖像素的顏色,只能取近似值,所以相同的圖片在 dpr>1的屏幕上就會(huì)模糊:

image

9.2 解決方案

為了保證圖片質(zhì)量,我們應(yīng)該盡可能讓一個(gè)屏幕像素來(lái)渲染一個(gè)圖片像素,所以,針對(duì)不同 DPR的屏幕,我們需要展示不同分辨率的圖片。

如:在 dpr=2的屏幕上展示兩倍圖 (@2x),在 dpr=3的屏幕上展示三倍圖 (@3x)

image

9.3 media查詢

使用 media查詢判斷不同的設(shè)備像素比來(lái)顯示不同精度的圖片:

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. .avatar{

  2. background-image: url(conardLi_1x.png);

  3. }

  4. @media only screen and (-webkit-min-device-pixel-ratio:2){

  5. .avatar{

  6. background-image: url(conardLi_2x.png);

  7. }

  8. }

  9. @media only screen and (-webkit-min-device-pixel-ratio:3){

  10. .avatar{

  11. background-image: url(conardLi_3x.png);

  12. }

  13. }

</pre>

只適用于背景圖

9.4 image-set

使用 image-set

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. .avatar {

  2. background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x );

  3. }

</pre>

只適用于背景圖

9.5 srcset

使用 img標(biāo)簽的 srcset屬性,瀏覽器會(huì)自動(dòng)根據(jù)像素密度匹配最佳顯示圖片:

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. <img src="conardLi_1x.png"

  2. srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x">

</pre>

9.6 JavaScript拼接圖片url

使用 window.devicePixelRatio獲取設(shè)備像素比,遍歷所有圖片,替換圖片地址:

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. const dpr = window.devicePixelRatio;

  2. const images = document.querySelectorAll('img');

  3. images.forEach((img)=>{

  4. img.src.replace(".",@${dpr}x.);

  5. })

</pre>

9.7 使用svg

SVG的全稱是可縮放矢量圖( ScalableVectorGraphics)。不同于位圖的基于像素, SVG 則是屬于對(duì)圖像的形狀描述,所以它本質(zhì)上是文本文件,體積較小,且不管放大多少倍都不會(huì)失真。

image

除了我們手動(dòng)在代碼中繪制 svg,我們還可以像使用位圖一樣使用 svg圖片:

<pre class="" style="margin: 0px; padding: 8px 0px 6px; max-width: 100%; box-sizing: border-box; word-wrap: break-word !important; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: 0.544000029563904px; orphans: auto; text-indent: 0px; text-transform: none; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; border-radius: 0px; overflow-y: auto; color: rgb(80, 97, 109); text-align: start; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, 'Microsoft Yahei' !important; border: 1px solid rgb(226, 226, 226) !important; background: rgb(241, 239, 238);">

  1. <img src="conardLi.svg">

  2. <img src="data:image/svg+xml;base64,[data]">

  3. .avatar {

  4. background: url(conardLi.svg);

  5. }

</pre>

參考

小結(jié)

希望你閱讀本篇文章后可以達(dá)到以下幾點(diǎn):

  • 理清移動(dòng)端適配常用概念

  • 理解移動(dòng)端適配問(wèn)題產(chǎn)生的原理,至少掌握一種解決方案

文中如有錯(cuò)誤,歡迎留言指正。

作者:ConardLi
鏈接:https://segmentfault.com/a/1190000019207842

求點(diǎn)贊,求關(guān)注~


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評(píng)論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,744評(píng)論 3 421
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,879評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,181評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,935評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,325評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評(píng)論 3 443
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,534評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,084評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,892評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,067評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,322評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,735評(píng)論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,990評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,800評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,084評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容

  • 移動(dòng)端適配方案:1)viewport(scale=1/dpr)2)rem3)flex4)vm/vh一、什么是移動(dòng)端...
    puxiaotaoc閱讀 43,101評(píng)論 3 56
  • 一、meta標(biāo)簽的效果 移動(dòng)端頁(yè)面一般會(huì)在head頭部添加如下meta標(biāo)簽。 該meta標(biāo)簽是否添加對(duì)頁(yè)面渲染的影...
    nimw閱讀 3,598評(píng)論 0 5
  • 不知不覺(jué)做前端已經(jīng)兩年了,從PC端,移動(dòng)端,微信小程序一路走來(lái)到今天剛剛開(kāi)放注冊(cè)的快應(yīng)用(手機(jī)廠商對(duì)抗小程序的新技...
    是ADI呀閱讀 3,149評(píng)論 0 10
  • title: 移動(dòng)端Web頁(yè)面適配淺析date: 2018-01-31 16:38:01tags: 移動(dòng)端 適配 ...
    豆板兒閱讀 12,312評(píng)論 0 16
  • 剛開(kāi)始做移動(dòng)端web開(kāi)發(fā)的同學(xué)應(yīng)該都碰到過(guò)頁(yè)面適配問(wèn)題,為什么我在開(kāi)發(fā)手機(jī)上調(diào)試好的頁(yè)面在其他手機(jī)會(huì)有這樣或那樣的...
    留七七閱讀 19,450評(píng)論 5 80