導讀
移動端適配,是我們在開發中經常會遇到的,這裡面可能會遇到非常多的問題:
1px
UI
iPhoneX
上面這些問題可能我們在開發中已經知道如何解決,但是問題產生的原理,以及解決方案的原理可能會模糊不清。在解決這些問題的過程中,我們往往會遇到非常多的概念:像素、解析度、PPI、DPI、DP、DIP、DPR、視口等等,你真的能分清這些概念的意義嗎?
PPI
DPI
DP
DIP
DPR
本文將從移動端適配的基礎概念出發,探究移動端適配各種問題的解決方案和實現原理。
一般用英寸描述屏幕的物理大小,如電腦顯示器的17、22,手機顯示器的4.8、5.7等使用的單位都是英寸。
17
22
4.8
5.7
需要注意,上面的尺寸都是屏幕對角線的長度:
英寸(inch,縮寫為in)在荷蘭語中的本意是大拇指,一英寸就是指甲底部普通人拇指的寬度。
inch
in
英寸和厘米的換算:1英寸 = 2.54 厘米
1英寸 = 2.54 厘米
像素即一個小方塊,它具有特定的位置和顏色。
圖片、電子屏幕(手機、電腦)就是由無數個具有特定顏色和特定位置的小方塊拼接而成。
像素可以作為圖片或電子屏幕的最小組成單位。
下面我們使用sketch打開一張圖片:
sketch
將這些圖片放大即可看到這些像素點:
通常我們所說的解析度有兩種,屏幕解析度和圖像解析度。
屏幕解析度指一個屏幕具體由多少個像素點組成。
下面是apple的官網上對手機解析度的描述:
apple
iPhone XS Max 和 iPhone SE的解析度分別為2688 x 1242和1136 x 640。這表示手機分別在垂直和水平上所具有的像素點數。
iPhone XS Max
iPhone SE
2688 x 1242
1136 x 640
當然解析度高不代表屏幕就清晰,屏幕的清晰程度還與尺寸有關。
我們通常說的圖片解析度其實是指圖片含有的像素數,比如一張圖片的解析度為800 x 400。這表示圖片分別在垂直和水平上所具有的像素點數為800和400。
圖片解析度
像素數
800 x 400
800
400
同一尺寸的圖片,解析度越高,圖片越清晰。
PPI(Pixel Per Inch):每英寸包括的像素數。
PPI(Pixel Per Inch)
PPI可以用於描述屏幕的清晰度以及一張圖片的質量。
使用PPI描述圖片時,PPI越高,圖片質量越高,使用PPI描述屏幕時,PPI越高,屏幕越清晰。
在上面描述手機解析度的圖片中,我們可以看到:iPhone XS Max 和 iPhone SE的PPI分別為458和326,這足以證明前者的屏幕更清晰。
458
326
由於手機尺寸為手機對角線的長度,我們通常使用如下的方法計算PPI:
$$ frac{sqrt{水平像素點數^2+垂直像素點數^2}}{尺寸}$$
iPhone 6的PPI為 $ frac{sqrt{1334^2+750^2}}{4.7}=325.6$,那它每英寸約含有326個物理像素點。
iPhone 6
DPI(Dot Per Inch):即每英寸包括的點數。
DPI(Dot Per Inch)
這裡的點是一個抽象的單位,它可以是屏幕像素點、圖片像素點也可以是印表機的墨點。
平時你可能會看到使用DPI來描述圖片和屏幕,這時的DPI應該和PPI是等價的,DPI最常用的是用於描述印表機,表示印表機每英寸可以列印的點數。
一張圖片在屏幕上顯示時,它的像素點數是規則排列的,每個像素點都有特定的位置和顏色。
當使用印表機進行列印時,印表機可能不會規則的將這些點列印出來,而是使用一個個列印點來呈現這張圖像,這些列印點之間會有一定的空隙,這就是DPI所描述的:列印點的密度。
在上面的圖像中我們可以清晰的看到,印表機是如何使用墨點來列印一張圖像。
所以,印表機的DPI越高,列印圖像的精細程度就越高,同時這也會消耗更多的墨點和時間。
實際上,上面我們描述的像素都是物理像素,即設備上真實的物理單元。
物理像素
下面我們來看看設備獨立像素究竟是如何產生的:
設備獨立像素
智能手機發展非常之快,在幾年之前,我們還用著解析度非常低的手機,比如下面左側的白色手機,它的解析度是320x480,我們可以在上面瀏覽正常的文字、圖片等等。
320x480
但是,隨著科技的發展,低解析度的手機已經不能滿足我們的需求了。很快,更高解析度的屏幕誕生了,比如下面的黑色手機,它的解析度是640x940,正好是白色手機的兩倍。
640x940
理論上來講,在白色手機上相同大小的圖片和文字,在黑色手機上會被縮放一倍,因為它的解析度提高了一倍。這樣,豈不是後面出現更高解析度的手機,頁面元素會變得越來越小嗎?
然而,事實並不是這樣的,我們現在使用的智能手機,不管解析度多高,他們所展示的界面比例都是基本類似的。喬布斯在iPhone4的發布會上首次提出了Retina Display(視網膜屏幕)的概念,它正是解決了上面的問題,這也使它成為一款跨時代的手機。
iPhone4
Retina Display
在iPhone4使用的視網膜屏幕中,把2x2個像素當1個像素使用,這樣讓屏幕看起來更精緻,但是元素的大小卻不會改變。
2x2
1
如果黑色手機使用了視網膜屏幕的技術,那麼顯示結果應該是下面的情況,比如列表的寬度為300個像素,那麼在一條水平線上,白色手機會用300個物理像素去渲染它,而黑色手機實際上會用600個物理像素去渲染它。
300
600
我們必須用一種單位來同時告訴不同解析度的手機,它們在界面上顯示元素的大小是多少,這個單位就是設備獨立像素(Device Independent Pixels)簡稱DIP或DP。上面我們說,列表的寬度為300個像素,實際上我們可以說:列表的寬度為300個設備獨立像素。
Device Independent Pixels
打開chrome的開發者工具,我們可以模擬各個手機型號的顯示情況,每種型號上面會顯示一個尺寸,比如iPhone X顯示的尺寸是375x812,實際iPhone X的解析度會比這高很多,這裡顯示的就是設備獨立像素。
chrome
iPhone X
375x812
設備像素比device pixel ratio簡稱dpr,即物理像素和設備獨立像素的比值。
device pixel ratio
dpr
在web中,瀏覽器為我們提供了window.devicePixelRatio來幫助我們獲取dpr。
web
window.devicePixelRatio
在css中,可以使用媒體查詢min-device-pixel-ratio,區分dpr:
css
min-device-pixel-ratio
@media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2){ }
在React Native中,我們也可以使用PixelRatio.get()來獲取DPR。
React Native
PixelRatio.get()
當然,上面的規則也有例外,iPhone 6、7、8 Plus的實際物理像素是1080 x 1920,在開發者工具中我們可以看到:它的設備獨立像素是414 x 736,設備像素比為3,設備獨立像素和設備像素比的乘積並不等於1080 x 1920,而是等於1242 x 2208。
iPhone 6、7、8 Plus
1080 x 1920
414 x 736
3
1242 x 2208
實際上,手機會自動把1242 x 2208個像素點塞進1080 * 1920個物理像素點來渲染,我們不用關心這個過程,而1242 x 2208被稱為屏幕的設計像素。我們開發過程中也是以這個設計像素為準。
1080 * 1920
設計像素
實際上,從蘋果提出視網膜屏幕開始,才出現設備像素比這個概念,因為在這之前,移動設備都是直接使用物理像素來進行展示。
緊接著,Android同樣使用了其他的技術方案來實現DPR大於1的屏幕,不過原理是類似的。由於Android屏幕尺寸非常多、解析度高低跨度非常大,不像蘋果只有它自己的幾款固定設備、尺寸。所以,為了保證各種設備的顯示效果,Android按照設備的像素密度將設備分成了幾個區間:
Android
當然,所有的Android設備不一定嚴格按照上面的解析度,每個類型可能對應幾種不同解析度,所以,每個Android手機都能根據給定的區間範圍,確定自己的DPR,從而擁有類似的顯示。當然,僅僅是類似,由於各個設備的尺寸、解析度上的差異,設備獨立像素也不會完全相等,所以各種Android設備仍然不能做到在展示上完全相等。
在iOS、Android和React Native開發中樣式單位其實都使用的是設備獨立像素。
iOS
iOS的尺寸單位為pt,Android的尺寸單位為dp,React Native中沒有指定明確的單位,它們其實都是設備獨立像素dp。
pt
dp
在使用React Native開發App時,UI給我們的原型圖一般是基於iphone6的像素給定的。
App
iphone6
為了適配所有機型,我們在寫樣式時需要把物理像素轉換為設備獨立像素:例如:如果給定一個元素的高度為200px(這裡的px指物理像素,非CSS像素),iphone6的設備像素比為2,我們給定的height應為200px/2=100dp。
200px
px
CSS
2
height
200px/2=100dp
當然,最好的是,你可以和設計溝通好,所有的UI圖都按照設備獨立像素來出。
我們還可以在代碼(React Native)中進行px和dp的轉換:
import {PixelRatio } from react-native;
const dpr = PixelRatio.get();
/** * px轉換為dp */ export function pxConvertTodp(px) { return px / dpr; }
/** * dp轉換為px */ export function dpConvertTopx(dp) { return PixelRatio.getPixelSizeForLayoutSize(dp); }
在寫CSS時,我們用到最多的單位是px,即CSS像素,當頁面縮放比例為100%時,一個CSS像素等於一個設備獨立像素。
CSS像素
100%
但是CSS像素是很容易被改變的,當用戶對瀏覽器進行了放大,CSS像素會被放大,這時一個CSS像素會跨越更多的物理像素。
頁面的縮放係數 = CSS像素 / 設備獨立像素。
頁面的縮放係數 = CSS像素 / 設備獨立像素
這裡多說兩句Retina屏幕,因為我在很多文章中看到對Retina屏幕的誤解。
Retina
Retina屏幕只是蘋果提出的一個營銷術語:
在普通的使用距離下,人的肉眼無法分辨單個的像素點。
為什麼強調普通的使用距離下呢?我們來看一下它的計算公式:
普通的使用距離下
$$ a=2arctan(h/2d) $$
a代表人眼視角,h代表像素間距,d代表肉眼與屏幕的距離,符合以上條件的屏幕可以使肉眼看不見單個物理像素點。
a
h
d
它不能單純的表達解析度和PPI,只能一種表達視覺效果。
讓多個物理像素渲染一個獨立像素只是Retina屏幕為了達到效果而使用的一種技術。而不是所有DPR > 1的屏幕就是Retina屏幕。
DPR > 1
比如:給你一塊超大尺寸的屏幕,即使它的PPI很高,DPR也很高,在近距離你也能看清它的像素點,這就不算Retina屏幕。
我們經常見到用K和P這個單位來形容屏幕:
K
P
P代表的就是屏幕縱向的像素個數,1080P即縱向有1080個像素,解析度為1920X1080的屏幕就屬於1080P屏幕。
1080P
1080
1920X1080
我們平時所說的高清屏其實就是屏幕的物理解析度達到或超過1920X1080的屏幕。
K代表屏幕橫向有幾個1024個像素,一般來講橫向像素超過2048就屬於2K屏,橫向像素超過4096就屬於4K屏。
1024
2048
2K
4096
4K
視口(viewport)代表當前可見的計算機圖形區域。在Web瀏覽器術語中,通常與瀏覽器窗口相同,但不包括瀏覽器的UI, 菜單欄等——即指你正在瀏覽的文檔的那一部分。
viewport
Web
一般我們所說的視口共包括三種:布局視口、視覺視口和理想視口,它們在屏幕適配中起著非常重要的作用。
布局視口(layout viewport):當我們以百分比來指定一個元素的大小時,它的計算值是由這個元素的包含塊計算而來的。當這個元素是最頂級的元素時,它就是基於布局視口來計算的。
layout viewport
所以,布局視口是網頁布局的基準窗口,在PC瀏覽器上,布局視口就等於當前瀏覽器的窗口大小(不包括borders 、margins、滾動條)。
PC
borders
margins
在移動端,布局視口被賦予一個默認值,大部分為980px,這保證PC的網頁可以在手機瀏覽器上呈現,但是非常小,用戶可以手動對網頁進行放大。
980px
我們可以通過調用document.documentElement.clientWidth / clientHeight來獲取布局視口大小。
document.documentElement.clientWidth / clientHeight
視覺視口(visual viewport):用戶通過屏幕真實看到的區域。
visual viewport
視覺視口默認等於當前瀏覽器的窗口大小(包括滾動條寬度)。
當用戶對瀏覽器進行縮放時,不會改變布局視口的大小,所以頁面布局是不變的,但是縮放會改變視覺視口的大小。
例如:用戶將瀏覽器窗口放大了200%,這時瀏覽器窗口中的CSS像素會隨著視覺視口的放大而放大,這時一個CSS像素會跨越更多的物理像素。
200%
所以,布局視口會限制你的CSS布局而視覺視口決定用戶具體能看到什麼。
我們可以通過調用window.innerWidth / innerHeight來獲取視覺視口大小。
window.innerWidth / innerHeight
布局視口在移動端展示的效果並不是一個理想的效果,所以理想視口(ideal viewport)就誕生了:網站頁面在移動端展示的理想大小。
ideal viewport
如上圖,我們在描述設備獨立像素時曾使用過這張圖,在瀏覽器調試移動端時頁面上給定的像素大小就是理想視口大小,它的單位正是設備獨立像素。
上面在介紹CSS像素時曾經提到頁面的縮放係數 = CSS像素 / 設備獨立像素,實際上說頁面的縮放係數 = 理想視口寬度 / 視覺視口寬度更為準確。
CSS像素時
頁面的縮放係數 = 理想視口寬度 / 視覺視口寬度
所以,當頁面縮放比例為100%時,CSS像素 = 設備獨立像素,理想視口 = 視覺視口。
CSS像素 = 設備獨立像素
理想視口 = 視覺視口
我們可以通過調用screen.width / height來獲取理想視口大小。
screen.width / height
<meta> 元素表示那些不能由其它HTML元相關元素之一表示的任何元數據信息,它可以告訴瀏覽器如何解析頁面。
<meta>
HTML
我們可以藉助<meta>元素的viewport來幫助我們設置視口、縮放等,從而讓移動端得到更好的展示效果。
<meta name="viewport" content="width_=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;">
上面是viewport的一個配置,我們來看看它們的具體含義:
Value| 可能值| 描述 -|-|- width| 正整數或device-width | 以pixels(像素)為單位, 定義布局視口的寬度。 height| 正整數或device-height | 以pixels(像素)為單位, 定義布局視口的高度。 initial-scale| 0.0 - 10.0|定義頁面初始縮放比率。 minimum-scale| 0.0 - 10.0|定義縮放的最小值;必須小於或等於maximum-scale的值。 maximum-scale| 0.0 - 10.0|定義縮放的最大值;必須大於或等於minimum-scale的值。 user-scalable| 一個布爾值(yes或者no)| 如果設置為 no,用戶將不能放大或縮小網頁。默認值為 yes。
Value
width
device-width
pixels
device-height
initial-scale
0.0 - 10.0
minimum-scale
maximum-scale
user-scalable
yes
no
為了在移動端讓頁面獲得更好的顯示效果,我們必須讓布局視口、視覺視口都儘可能等於理想視口。
device-width就等於理想視口的寬度,所以設置width_=device-width就相當於讓布局視口等於理想視口。
width_=device-width
由於initial-scale = 理想視口寬度 / 視覺視口寬度,所以我們設置initial-scale=1;就相當於讓視覺視口等於理想視口。
initial-scale = 理想視口寬度 / 視覺視口寬度
initial-scale=1;
這時,1個CSS像素就等於1個設備獨立像素,而且我們也是基於理想視口來進行布局的,所以呈現出來的頁面布局在各種設備上都能大致相似。
上面提到width可以決定布局視口的寬度,實際上它並不是布局視口的唯一決定性因素,設置initial-scale也有肯能影響到布局視口,因為布局視口寬度取的是width和視覺視口寬度的最大值。
例如:若手機的理想視口寬度為400px,設置width_=device-width,initial-scale=2,此時視覺視口寬度 = 理想視口寬度 / initial-scale即200px,布局視口取兩者最大值即device-width 400px。
400px
initial-scale=2
視覺視口寬度 = 理想視口寬度 / initial-scale
若設置width_=device-width,initial-scale=0.5,此時視覺視口寬度 = 理想視口寬度 / initial-scale即800px,布局視口取兩者最大值即800px。
initial-scale=0.5
800px
瀏覽器為我們提供的獲取窗口大小的API有很多,下面我們再來對比一下:
API
window.innerHeight
window.outerHeight
window.screen.Height
設備的解析度/設備像素比
window.screen.availHeight
document.documentElement.clientHeight
document.documentElement.offsetHeight
document.documentElement.scrollHeight
clientHeight
為了適配各種屏幕,我們寫代碼時一般使用設備獨立像素來對頁面進行布局。
而在設備像素比大於1的屏幕上,我們寫的1px實際上是被多個物理像素渲染,這就會出現1px在有些屏幕上看起來很粗的現象。
基於media查詢判斷不同的設備像素比給定不同的border-image:
media
border-image
.border_1px{ border-bottom: 1px solid #000; } @media only screen and (-webkit-min-device-pixel-ratio:2){ .border_1px{ border-bottom: none; border-width: 0 0 1px 0; border-image: url(../img/1pxline.png) 0 0 2 0 stretch; } }
和border-image類似,準備一張符合條件的邊框背景圖,模擬在背景上。
.border_1px{ border-bottom: 1px solid #000; } @media only screen and (-webkit-min-device-pixel-ratio:2){ .border_1px{ background: url(../img/1pxline.png) repeat-x left bottom; background-size: 100% 1px; } }
上面兩種都需要單獨準備圖片,而且圓角不是很好處理,但是可以應對大部分場景。
基於media查詢判斷不同的設備像素比對線條進行縮放:
.border_1px:before{ content: ; position: absolute; top: 0; height: 1px; width: 100%; background-color: #000; transform-origin: 50% 0%; } @media only screen and (-webkit-min-device-pixel-ratio:2){ .border_1px:before{ transform: scaleY(0.5); } } @media only screen and (-webkit-min-device-pixel-ratio:3){ .border_1px:before{ transform: scaleY(0.33); } }
這種方式可以滿足各種場景,如果需要滿足圓角,只需要給偽類也加上border-radius即可。
border-radius
上面我們border-image和background-image都可以模擬1px邊框,但是使用的都是點陣圖,還需要外部引入。
background-image
藉助PostCSS的postcss-write-svg我們能直接使用border-image和background-image創建svg的1px邊框:
PostCSS
postcss-write-svg
svg
@svg border_1px { height: 2px; @rect { fill: var(--color, black); width: 100%; height: 50%; } } .example { border: 1px solid transparent; border-image: svg(border_1px param(--color #00b1ff)) 2 2 stretch; }
編譯後:
.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; }
上面的方案是大漠在他的文章中推薦使用的,基本可以滿足所有場景,而且不需要外部引入,這是我個人比較喜歡的一種方案。
通過設置縮放,讓CSS像素等於真正的物理像素。
例如:當設備像素比為3時,我們將頁面縮放1/3倍,這時1px等於一個真正的屏幕像素。
1/3
const scale = 1 / window.devicePixelRatio; const viewport = document.querySelector(meta[name="viewport"]); if (!viewport) { viewport = document.createElement(meta); viewport.setAttribute(name, viewport); window.document.head.appendChild(viewport); } viewport.setAttribute(content, width_=device-width,user-scalable=no,initial-scale= + scale + ,maximum-scale= + scale + ,minimum-scale= + scale);
實際上,上面這種方案是早先flexible採用的方案。
flexible
當然,這樣做是要付出代價的,這意味著你頁面上所有的布局都要按照物理像素來寫。這顯然是不現實的,這時,我們可以藉助flexible或vw、vh來幫助我們進行適配。
vw、vh
儘管我們可以使用設備獨立像素來保證各個設備在不同手機上顯示的效果類似,但這並不能保證它們顯示完全一致,我們需要一種方案來讓設計稿得到更完美的適配。
flexible方案是阿里早期開源的一個移動端適配解決方案,引用flexible後,我們在頁面上統一使用rem來布局。
rem
它的核心代碼非常簡單:
// set 1rem = viewWidth / 10 function setRemUnit () { var rem = docEl.clientWidth / 10 docEl.style.fontSize = rem + px } setRemUnit();
rem 是相對於html節點的font-size來做計算的。
html
font-size
我們通過設置document.documentElement.style.fontSize就可以統一整個頁面的布局標準。
document.documentElement.style.fontSize
上面的代碼中,將html節點的font-size設置為頁面clientWidth(布局視口)的1/10,即1rem就等於頁面布局視口的1/10,這就意味著我們後面使用的rem都是按照頁面比例來計算的。
clientWidth
1/10
1rem
這時,我們只需要將UI出的圖轉換為rem即可。
以iPhone6為例:布局視口為375px,則1rem = 37.5px,這時UI給定一個元素的寬為75px(設備獨立像素),我們只需要將它設置為75 / 37.5 = 2rem。
iPhone6
375px
1rem = 37.5px
75px
75 / 37.5 = 2rem
當然,每個布局都要計算非常繁瑣,我們可以藉助PostCSS的px2rem插件來幫助我們完成這個過程。
px2rem
下面的代碼可以保證在頁面大小變化時,布局可以自適應,當觸發了window的resize和pageShow事件之後自動調整html的fontSize大小。
window
resize
pageShow
fontSize
// reset rem unit on page resize window.addEventListener(resize, setRemUnit)window.addEventListener(pageshow, function (e) { if (e.persisted) { setRemUnit() } })
由於viewport單位得到眾多瀏覽器的兼容,上面這種方案現在已經被官方棄用:
lib-flexible這個過渡方案已經可以放棄使用,不管是現在的版本還是以前的版本,都存有一定的問題。建議大家開始使用viewport來替代此方案。
下面我們來看看現在最流行的vh、vw方案。
vh、vw
vh、vw方案即將視覺視口寬度 window.innerWidth和視覺視口高度 window.innerHeight 等分為 100 份。
window.innerWidth
上面的flexible方案就是模仿這種方案,因為早些時候vw還沒有得到很好的兼容。
vw
vw(Viewports width)
1vw
1%
vh(Viewports height)
1vh
vmin
vh
vmax
如果視覺視口為375px,那麼1vw = 3.75px,這時UI給定一個元素的寬為75px(設備獨立像素),我們只需要將它設置為75 / 3.75 = 20vw。
1vw = 3.75px
75 / 3.75 = 20vw
這裡的比例關係我們也不用自己換算,我們可以使用PostCSS的 postcss-px-to-viewport 插件幫我們完成這個過程。寫代碼時,我們只需要根據UI給的設計圖寫px單位即可。
postcss-px-to-viewport
當然,沒有一種方案是十全十美的,vw同樣有一定的缺陷:
margin
100vw
padding
calc()
iPhoneX的出現將手機的顏值帶上了一個新的高度,它取消了物理按鍵,改成了底部的小黑條,但是這樣的改動給開發者適配移動端又增加了難度。
在iPhoneX發布後,許多廠商相繼推出了具有邊緣屏幕的手機。
這些手機和普通手機在外觀上無外乎做了三個改動:圓角(corners)、劉海(sensor housing)和小黑條(Home Indicator)。為了適配這些手機,安全區域這個概念變誕生了:安全區域就是一個不受上面三個效果的可視窗口範圍。
corners
sensor housing
Home Indicator
為了保證頁面的顯示效果,我們必須把頁面限制在安全範圍內,但是不影響整體效果。
viewport-fit是專門為了適配iPhoneX而誕生的一個屬性,它用於限制網頁如何在安全區域內進行展示。
viewport-fit
contain: 可視窗口完全包含網頁內容
contain
cover:網頁內容完全覆蓋可視窗口
cover
默認情況下或者設置為auto和contain效果相同。
auto
我們需要將頂部和底部合理的擺放在安全區域內,iOS11新增了兩個CSS函數env、constant,用於設定安全區域與邊界的距離。
iOS11
env、constant
函數內部可以是四個常量:
safe-area-inset-left
safe-area-inset-right
safe-area-inset-top
safe-area-inset-bottom
注意:我們必須指定viweport-fit後才能使用這兩個函數:
viweport-fit
<meta name="viewport" content="viewport-fit=cover">
constant在iOS < 11.2的版本中生效,env在iOS >= 11.2的版本中生效,這意味著我們往往要同時設置他們,將頁面限制在安全區域內:
constant
iOS < 11.2
env
iOS >= 11.2
body { padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); }
當使用底部固定導航欄時,我們要為他們設置padding值:
{ padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); }
很多視口我們要對橫屏和豎屏顯示不同的布局,所以我們需要檢測在不同的場景下給定不同的樣式:
window.orientation:獲取屏幕旋轉方向
window.orientation
window.addEventListener("resize", ()=>{ if (window.orientation === 180 || window.orientation === 0) { // 正常方向或屏幕旋轉180度 console.log(豎屏); }; if (window.orientation === 90 || window.orientation === -90 ){ // 屏幕順時鐘旋轉90度或屏幕逆時針旋轉90度 console.log(橫屏); } });
@media screen and (orientation: portrait) { /*豎屏...*/ } @media screen and (orientation: landscape) { /*橫屏...*/ }
我們平時使用的圖片大多數都屬於點陣圖(png、jpg...),點陣圖由一個個像素點構成的,每個像素都具有特定的位置和顏色值:
png、jpg...
理論上,點陣圖的每個像素對應在屏幕上使用一個物理像素來渲染,才能達到最佳的顯示效果。
而在dpr > 1的屏幕上,點陣圖的一個像素可能由多個物理像素來渲染,然而這些物理像素點並不能被準確的分配上對應點陣圖像素的顏色,只能取近似值,所以相同的圖片在dpr > 1的屏幕上就會模糊:
dpr > 1
為了保證圖片質量,我們應該儘可能讓一個屏幕像素來渲染一個圖片像素,所以,針對不同DPR的屏幕,我們需要展示不同解析度的圖片。
如:在dpr=2的屏幕上展示兩倍圖(@2x),在dpr=3的屏幕上展示三倍圖(@3x)。
dpr=2
(@2x)
dpr=3
(@3x)
使用media查詢判斷不同的設備像素比來顯示不同精度的圖片:
.avatar{ background-image: url(conardLi_1x.png); } @media only screen and (-webkit-min-device-pixel-ratio:2){ .avatar{ background-image: url(conardLi_2x.png); } } @media only screen and (-webkit-min-device-pixel-ratio:3){ .avatar{ background-image: url(conardLi_3x.png); } }
只適用於背景圖
使用image-set:
image-set
.avatar { background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x ); }
使用img標籤的srcset屬性,瀏覽器會自動根據像素密度匹配最佳顯示圖片:
img
srcset
<img src="conardLi_1x.png" srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x">
使用window.devicePixelRatio獲取設備像素比,遍歷所有圖片,替換圖片地址:
const dpr = window.devicePixelRatio; const images = document.querySelectorAll(img); images.forEach((img)=>{ img.src.replace(".", `@${dpr}x.`); })
SVG的全稱是可縮放矢量圖(Scalable Vector Graphics)。不同於點陣圖的基於像素,SVG 則是屬於對圖像的形狀描述,所以它本質上是文本文件,體積較小,且不管放大多少倍都不會失真。
SVG
Scalable Vector Graphics
除了我們手動在代碼中繪製svg,我們還可以像使用點陣圖一樣使用svg圖片:
<img src="conardLi.svg">
<img src="data:image/svg+xml;base64,[data]">
.avatar { background: url(conardLi.svg); }
希望你閱讀本篇文章後可以達到以下幾點:
文中如有錯誤,歡迎在評論區指正,如果這篇文章幫助到了你,歡迎點贊和關注。
想閱讀更多優質文章、可關注我的github博客,你的star?、點贊和關注是我持續創作的動力!
推薦關注我的微信公眾號【code秘密花園】,每天推送高質量文章,我們一起交流成長。