接:https://www.jianshu.com/p/3b45aa981e77
下面是一些基礎概念的講解,幫助理解各種適配方案實現。
像素:
1、物理像素(設備像素)
屏幕的物理像素,又被稱為設備像素,他是顯示設備中一個最微小的物理部件。任何設備屏幕的物理像素出廠時就確定了,且固定不變的。
2、設備獨立像素
設備獨立像素也稱為密度無關像素,可以認為是計算機坐標系統中的一個點,這個點代表一個可以由程序使用的虛擬像素(比如說CSS像素),然后由相關系統轉換為物理像素。
3、設備像素比
設備像素比簡稱為dpr,其定義了物理像素和設備獨立像素的對應關系
設備像素比 = 物理像素 / 設備獨立像素 以iphone6為例: iphone6的設備寬和高為375pt * 667pt,可以理解為設備的獨立像素,而其設備像素比為2.固有設備像素為750pt * 1334pt
通過:window.devicePixelRatio獲得。
設備像素比是區別是否是高清屏的標準,dpr大于1時就為高清屏,一般情況下dpr為整數,但是android有些奇葩機型不為整數。
4、css像素
在CSS、JS中使用的一個長度單位。單位px
注:在pc端1物理像素等于1px,但是移動端1物理像素不一定等于1px,1物理像素與px的關系與以下因素有關。(有些視口概念,可以把下面視口看完了再來看)
1、屏幕布局視口大小(下面會講到) 2、屏幕的分辨率(物理像素)
對于一塊屏幕,其物理像素是確定的。視覺視口尺寸是繼承的布局視口的,而視覺視口里寬度即是css的px數。故在一塊屏上物理像素與px的關系就是物理像素與布局視口的px數的關系。
比如iphone6,期物理像素為750,如果沒有設置布局視口時,viewport為980px 此時:1物理像素長度等于980/750px = 1.3067px的長度 由于像素都是點陣的,故1物理像素相當于1.3067px * 1.3067px方格。 當在meta中設置了如下配置時 <meta name="viewport" content="width=device-width"> 相當于把布局視口設置為設備的寬度(即上面講到的設備獨立像素), 對于iphone6就是375px。 此時1物理像素長度等于375/750px = 0.5px的長度,故1物理像素相當于0.5px * 0.5px的方格。
視口:
1、布局視口:
在html中一般在meta中的name為viewport字段就是控制的布局視口。布局視口一般都是瀏覽器廠商給的一個值。在手機互聯網沒有普及前,網絡上絕大部分頁面都是為電腦端瀏覽而做的,根本沒有做移動端的適配。隨著移動端的發展,在手機上看電腦端的頁面已成為非常普及現象。而電腦端頁面寬度較大,移動端寬度有限,要想看到整個網頁,會有很長的滾動條,看起來非常麻煩。于是瀏覽器廠商為了讓用戶在小屏幕下網頁也能夠顯示地很好,所以把布局視口設置的很大,一般在768px ~ 1024px 之間,最常用的寬度就是 980。這樣用戶就能看到絕大部分內容,并根據具體內容選擇縮放。
故布局視口是看不見的,瀏覽器廠商設置的一個固定值,如980px,并將980px的內容縮放到手機屏內。
布局視口可以通過:
document.documentElement.clientWidth(clientHeight) // 布局視口的尺寸。
2、視覺視口:
瀏覽器可視區域的大小,即用戶看到的網頁的區域。(其寬度繼承的布局視口寬度)
window.innerWidth(innerHeight) // 視覺視口尺寸
3、理想視口:
布局視口雖然解決了移動端查看pc端網頁的問題,但是完全忽略了手機本身的尺寸。所以蘋果引入了理想視口,它對設備來說是最理想的布局視口,用戶不需要對頁面進行縮放就能完美的顯示整個頁面。最簡單的做法就是使布局視口寬度改成屏幕的寬度。
可以通過window.screen.width獲取。
<meta name="viewport" content="width=device-width">
移動端到底怎么適配不同的屏幕呢?最簡單的方法是設置如下視口:
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
當使用以上方案定義布局視口時,即布局視口等于理想視口(屏幕寬度),屏幕沒有滾動條,不存在高清屏下,字體較小的問題。但是在不同屏幕上,其視覺寬度是不同的,不能簡單的將所有的尺寸都設置為px,可能會出現滾動條。小尺寸的可以用px,大尺寸的只能用百分比和彈性布局。
viewport縮放
對于上面的設置,再不同的屏幕上,css像素對應的物理像素具數是不一致的。
在普通屏幕下,dpr=1時,
1個css像素長度對應1個物理像素長度,1個css像素對應1個物理像素。
而在Retina屏幕下,如果dpr=2,
1個css像素長度對應2個物理像素長度,1css像素對應4個物理像素。
此時如果css中寫
border: 1px solid red; // 此時1px 對應的寬度是2物理像素的寬度。
而一般現在移動端設計稿都是基于iphone設計的,稿子一般為750px或640px,這正好是iphone6和iphone5的物理像素。在設計稿中,一般有些邊框效果,這時邊框的線寬為1px,對應的就是1物理像素。而對于iphone5和iphone6,當width=device-width時,css的1px顯示出來的是2個物理像素,所以看起來線就比較粗。怎么解決呢?1px邊框效果其實有很多hack方法,其中一種就是通過縮放viewport。
initial-scale是將布局視口進行縮放,initial-scale是相對于理想視口的,即initial-scale=1與width=device-width是一樣的效果。initial-scale=0.5等效于width= 2倍的device-width,所以設置initial-scale和width都可以改變布局視口的大小。
<meta name="viewport" content="width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
對于iphone6當添加如上設置后,initial-scale=0.5,即將頁面縮小2倍后等于屏幕寬度。
布局視口width: width / 2 = 375px; width = 750px;
所以此時布局視口為750px,此時1px等于1物理像素。
適配方案:
上面講了一些基礎概念,下面講具體適配。
對于ui設計師給的一張設計稿,怎么將其還原到頁面上?對于不同手機屏幕,其dpr不同,屏幕尺寸也不同,考慮到各種情況,有很多適配方案,所以不同的適配方案,實現方法不同,處理復雜度也不同,還原程度也不同。
方案一:
固定高度,寬度自適應。
這種方案是目前使用較多的方案,也是相對較簡單的實現方案:
該方法使用了理想視口:
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
垂直方向使用固定的值,水平方向使用彈性布局,元素采用定值、百分比、flex布局等。這種方案相對簡單,還原度也非常低。
方案二:
固定布局視口寬度,使用viewport進行縮放
如:荔枝FM、網易應用
荔枝的代碼:
if(/Android (\d+\.\d+)/.test(navigator.userAgent)){ var version = parseFloat(RegExp.$1); if(version>2.3){ var phoneScale = parseInt(window.screen.width)/640; if(/MZ-M571C/.test(navigator.userAgent)){ document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">'); }else if(/M571C/.test(navigator.userAgent)&&/LizhiFM/.test(navigator.userAgent)){ document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">'); }else{ document.write('<meta name="viewport" content="width=640, minimum-scale = '+ phoneScale +', maximum-scale = '+ phoneScale +', target-densitydpi=device-dpi">'); } }else{ document.write('<meta name="viewport" content="width=640, target-densitydpi=device-dpi">'); } }else{ document.write('<meta name="viewport" content="width=640, user-scalable=no, target-densitydpi=device-dpi">'); }
網易應用:
var win = window, width = 640, iw = win.innerWidth || width, ow = win.outerHeight || iw, sw = win.screen.width || iw, saw = win.screen.availWidth || iw, ih = win.innerHeight || width, oh = win.outerHeight || ih, ish = win.screen.height || ih, sah = win.screen.availHeight || ih, w = Math.min(iw, ow, sw, saw, ih, oh, ish, sah), ratio = w / width, dpr = win.devicePixelRatio; if (ratio = Math.min(ratio, dpr), 1 > ratio) { var ctt = ",initial-scale=" + ratio + ",maximum-scale=" + ratio, metas = document.getElementsByTagName("meta");ctt += ""; for (var i = 0, meta; i < metas.length; i++) meta = metas[i], "viewport" == meta.name && (meta.content += ctt) }
固定布局視口,寬度設置固定的值,總寬度為640px,根據屏幕寬度動態生成viewport。(設計稿應該是640px的)
<meta name="viewport" content="width=640, minimum-scale = 0.5625, maximum-scale = 0.5625, target-densitydpi=device-dpi">
這種方式布局如荔枝FM的網頁寬度始終為640px。縮放比例scale為:
var scale = window.screen.width / 640
設計稿為640px時,正好可以1:1以px來寫樣式。但是1px所對應的物理像素就不一定是1了。
(window.screen.width * dpr) / 640 // 1px對應的物理像素
iphone5.png
iphone6.png
方案三:
根據不同屏幕動態寫入font-size,以rem作為寬度單位,固定布局視口。
如網易新聞:
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
以640px設計稿和750px的視覺稿,網易這樣處理的:
var width = document.documentElement.clientWidth; // 屏幕的布局視口寬度 var rem = width / 7.5; // 750px設計稿將布局視口分為7.5份 var rem = width / 6.4; // 640px設計稿將布局視口分為6.4份
這樣不管是750px設計稿還是640px設計稿,1rem 等于設計稿上的100px。故px轉換rem時:
rem = px * 0.01;
在750px設計稿上:
75px 對應 0.75rem, 距離占設計稿的10%; 在ipone6上: width = document.documentElement.clientWidth = 375px; rem = 375px / 7.5 = 50px; 0.75rem = 37.5px; (37.5/375=10%;占屏幕10%) 在ipone5上: width = document.documentElement.clientWidth = 320px; rem = 320px / 7.5 = 42.667px; 0.75rem = 32px; (32/320=10%;占屏幕10%)
故對于設計稿上任何一個尺寸換成rem后,在任何屏下對應的尺寸占屏幕寬度的百分比相同。故這種布局可以百分比還原設計圖。
iphone5-2.png
iphone6-2.png
方案四:
以rem作為寬度單位,動態寫入viewport和font-size進行縮放。
根據設置的dpr設置font-size。如:
document.documentElement.style.fontSize = 50 * dpr; // dpr 為設置的設備像素比。(注意不是設備自身的設備像素比,而是認為設置的dpr)
這種情況下,dpr = 1時,1rem = 50px;
dpr = 2時, 1rem = 100px;
當設計以iphone6為標準,出750px的設計稿時,此時dpr=2,故1rem 等于100px,將圖上的尺寸轉換為rem非常方便,除以100就行。代碼如下:
var scale = 1.0; var dpr = 1; var isAndroid = window.navigator.appVersion.match(/android/gi); var isIPhone = window.navigator.appVersion.match(/iphone/gi); var devicePixelRatio = window.devicePixelRatio; // 此處只簡單對ios做了伸縮處理,安卓沒有做伸縮處理,統一dpr = 1 if ( isIPhone ) { scale /= devicePixelRatio; dpr *= devicePixelRatio; } var viewport = document.getElementById('viewport'); var content = 'initial-scale=' + scale + ', maximum-scale=' + scale + ',minimum-scale=' + scale + ', width=device-width, user-scalable=no'; viewport.setAttribute( 'content', content ); document.documentElement.style.fontSize = 50 * dpr + 'px'; document.documentElement.setAttribute('data-dpr', dpr);
對于該方案,
假設肉眼看到的寬度(視覺寬度):visualWidth,令dpr=1時,其1rem對應的寬度為50. dpr = 1 時, 1rem = 50px, initial-scale=1, 縮放為1。 visualWidth = 50 * 1 = 50; dpr = 2 時, 1rem = 100px, initial-scale=0.5, 縮放為0.5。 visualWidth = 100 * 0.5 = 50; dpr = 3 時, 1rem = 150px, initial-scale=0.3333, 縮放為0.3333。 visualWidth = 150 * 0.3333 = 50;
所以該方案,1rem在所有屏幕上對應的肉眼距離相同,故不同屏幕下,總的rem數不同,大屏下總的rem數大于小屏下,如iphone6下,總寬度為7.5rem,iphone5下,總寬度為6.4rem。故此方案不能百分比還原設計稿,故寫樣式時,對于大塊元素應該用百分比,flex等布局,不能直接用rem。
關于這個方案的詳細教程請參考這篇文章傳送門
iphone5-3.png
iphone6-3.png
方案五:
根據不同屏幕動態寫入font-size和viewport,以rem作為寬度單位
將屏幕分為固定的塊數10:
var width = document.documentElement.clientWidth; // 屏幕的布局視口寬度 var rem = width / 10; // 將布局視口分為10份
這樣在任何屏幕下,總長度都為10rem。1rem對應的值也不固定,與屏幕的布局視口寬度有關。
對于動態生成viewport,他們原理差不多,根據dpr來設置縮放。看看淘寶的:
var devicePixelRatio = window.devicePixelRatio; var isIPhone = window.navigator.appVersion.match(/iphone/gi); var dpr,scale; if (isIPhone) { if (devicePixelRatio >=3) { dpr = 3; } else if (devicePixelRatio >=2) { dpr = 2; } else { dpr = 1; } } else { dpr = 1; } scale = 1 / dpr;
淘寶只對iphone做了縮放處理,對于android所有dpr=1,scale=1即沒有縮放處理。
此方案與方案三相似,只是做了viewport縮放,能百分比還原設計稿。
iphone5-4.png
iphone6-4.png
適配中要解決的問題 :
移動端適配最主要的是使在不同屏幕下不用縮放頁面就能正常顯示整個頁面。以上方案都完成了這一需求。其次有幾個需求:
1、解決高清屏下1px的問題,其實有很多hack方法,這里只講了縮放視口。先將布局視口設置為高清屏的物理像素。這樣css中1px就是1個物理像素,這樣看到的線條才是真正的1px。但是此時視口寬度大于設備的寬度,就會出現滾動條。故對視口進行縮放,使視口寬度縮放到設備寬度。
淘寶團隊在處理安卓端的縮放存在很多問題,所以dpr都做1處理,所以安卓端就沒有解決1px的問題。
2、在大屏手機中一行看到的段落文字應該比小屏手機的多。
由于淘寶和網易新聞rem都是百分比,故如果用rem一行顯示的文字個數應該是相同的。故對于段落文本不能用rem作為單位,應該用px處理,對于不同的dpr下設置不同的字體。
.selector { color: red; font-size: 14px; } [data-dpr="2"] .selector { font-size: 28px; // 14 * 2 } [data-dpr="3"] .selector { font-size: 42px; // 14 * 3 }
對于方案四,不管什么情況下,1rem對應的視覺上的寬度都是一樣的,而對應的大屏、小屏手機其視覺寬度當然不同,故字體設置為rem單位時,也能滿足大屏手機一行顯示的字體較多這個需求。
五種方案對比:
上面四種方案對設計稿還原程度是有差別的。
除了方案一和方案四以外,其他方案都是百分比還原設計稿,大屏下元素的尺寸就大。
方案一還原設計稿程度較低,這里不做說明。
方案二做了百分比適配,部分1px適配,沒有字體適配。
方案三做了百分比適配,沒有1px適配,有字體大小適配。
方案四沒有做百分百適配,布局要用百分百和flex布局,做了1px的適配,并且對于段落文字直接可以用rem做單位,不需要做適配。
方案五做了百分比適配,有1px適配,有字體大小適配。
項目中遇到的問題:
在我們項目中方案四和方案五都用過。
方案五在使用中沒有遇到什么問題,就是剛開始沒有做字體適配都是用的rem,后面加入了字體適配,這種方案設計師相對輕松些,不用考慮在大小屏幕下的布局效果。
方案四時沒有跟ui設計師溝通清楚,導致設計師在設計圖上一行排了很多交互元素,在小屏下放不下去,又不能簡單放百分比(元素里的文字放不下)。所以還是要做動態判斷大小屏,做出相應適配。這個方案可能設計師需要考慮的多些,盡量減少一行內的交互元素,當一行交互元素多時要考慮小屏手機怎么適配。
其實對于1px的適配在蘋果端很好,在android端各個廠商手機差別太大,適配有很多問題。這是為什么絕大多數方案里都放棄了android端1px適配。不過最近看到很多網站都用了densitydpi=device-dpi這個安卓的私有屬性來兼容部分安卓機型,這個屬性在新的webkit已經被移除了,使用它主要為了兼容低版本的android系統。
這里大漠老師針對flexible方案進行了改版,兼容了更多的android機型的1px效果。文章傳送門
他給了個壓縮版的方案,我看了下源碼,把它寫了一遍,不知道有沒有問題,效果是一樣的。
var dpr, scale, timer, rem; var style = document.createElement('style'); dpr = window.devicePixelRatio || 1; scale = 1 / dpr; document.documentElement.setAttribute('data-dpr', dpr); var metaEl = document.createElement('meta'); metaEl.setAttribute('name', 'viewport'); metaEl.setAttribute('content', 'target-densitydpi=device-dpi, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no'); document.documentElement.firstElementChild.appendChild(metaEl); document.documentElement.firstElementChild.appendChild(style); if (980 === document.documentElement.clientWidth) { metaEl.setAttribute('content', 'target-densitydpi=device-dpi,width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1'); } function refreshRem () { var c = '}'; var width = document.documentElement.clientWidth; var isPhone = window.navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i); if (!isPhone && width > 1024) { width = 640; c = 'max-width:' + width + 'px;margin-right:auto!important;margin-left:auto!important;}'; } window.rem = rem = width / 16; style.innerHTML = 'html{font-size:' + rem + 'px!important;}body{font-size:' + parseInt(12 * (width / 320)) + 'px;' + c;; } refreshRem(); window.addEventListener('resize', function () { clearTimeout(timer); timer = setTimeout(refreshRem, 300); }, false); window.addEventListener('pageshow', function (e) { if (e.persisted) { clearTimeout(timer); timer = setTimeout(refreshRem, 300); } }, false);
這些方案只是針對絕大部分機型,項目中可能有些特殊機型有特殊問題,需要特殊對待。比如在這篇文章中作者使用flexible在小米max和榮耀8中有問題,需要特殊hack。傳送門,我沒有這種手機,也沒有對此做驗證。
對于上面的五種方案,方案五看似是適配最好的,但是當項目中引入第三方插件時可能要一一適配,比如:引入一個富文本,里面設置字體大小的一般都是px,你需要將其一一轉換成rem。而對于方案二,可以直接用px做單位來百分百還原設計稿,引入的插件時也不用適配。所以說,具體項目中用哪個方案,其實不光是前端的選擇,還要跟設計師討論下,滿足設計需求,選擇最適合項目的方案。
責編 | 郭芮
手機市場日漸豐富的同時,給我們前端開發人員帶來的 “網頁內容自適應屏幕尺寸進行顯示的問題” 也日漸凸顯出來,接下來我們就要細說移動端適配的前世今生及方案。
為什么要移動端適配?
一般情況下設計稿的設計師按照 375 的尺寸設計,然而,在現在移動終端(就是手機)快速更新的時代,每個品牌的手機都有著不同的物理分辨率,這樣就會導致,每臺設備的邏輯分辨率也不盡相同,此時 375 的設計稿,如果想要還原那基本是不可能了,因為如果一個左右布局,左邊如果寫死,右邊自適應的話,每個設備的右邊所展示的內容大小就不盡相同,這時移動端適配就顯得尤其重要。
既然要了解前世今生,我們就從幾個概念說起,先上一張圖。
下面我們一個個解析。
1.1 屏幕尺寸
屏幕尺寸是以屏幕對角線的長度來計量,計量單位為英寸。
如圖所示兩個對角線的長度就是這個屏幕的尺寸:
1.2 像素
我們看到上圖 320x480 叫分辨率,而這個所謂的分辨率說白了就是橫向320個像素縱向480個像素組成
1.2.1 什么叫像素呢?
像素(Pel, pixel, pictureelement),為組成一幅圖像的全部亮度和色度的最小圖像單元。電視的圖像是由按一定間隔排列的亮度不同的像點構成的,形成像點的單位也就是像素,組成圖像的最小單位就是像素。從計算機技術的角度來解釋,像素是硬件和軟件所能控制的最小單位。它指顯示屏的畫面上表示出來的最小單位,不是圖畫上的最小單位。一幅圖像通常包含成千上萬個像素,每個像素都有自己的顏色信息,它們緊密地組合在一起。由于人眼的錯覺,這些組合在一起的像素被當成一幅完整的圖像。當修改圖像的某區域,實際上是在修改該區域內的像素。對這些像素修改的好與壞將決定最終圖片的質量。單位面積內的像素越多,圖像的效果就越好。彩色電視圖像是由成千個像素點所組成的,而且每個像素都是由紅綠藍三種顏色并排組成的。(注意每個像素的大小是不固定的,他是根據設備的分辨率決定的,知識點,后面要考)
1.2.2 什么叫分辨率呢?
屏幕分辨率是指縱橫向上的像素點數,單位是 px。屏幕分辨率確定計算機屏幕上顯示多少信息的設置,以水平和垂直像素來衡量。就相同大小的屏幕而言,當屏幕分辨率低時(例如 640 x 480),在屏幕上顯示的像素少,單個像素尺寸比較大。屏幕分辨率高時(例如 1600 x 1200),在屏幕上顯示的像素多,單個像素尺寸比較小。
知道什么叫做分辨率后,有人就會奇怪,我記得蘋果的蘋果官網上的蘋果 6 的分辨率為 750x1334 啊,但是設計稿上蘋果 6 的分辨率為 375x667 啊,而且各個設備的分辨率都比實際分辨率小很多,這就牽扯到一些歷史原因了
1.2.3 設備物理分辨率(設備像素)
相信我們所有前端開發者,都是見證了手機這個移動設備發展的過程。從藍屏手機,到彩屏手機,到諾基亞研發出來觸屏手機,再到智能手機一步步發展下來,我們的我們的手越來越清晰,越來越大,所以我們的屏幕發展也越來越迅速。
上圖可以清楚的看到,不同分辨率所帶來的的差距。
從最初的顆粒感相當大的屏幕,到 720p 再到 1080p,甚至于現在各家旗艦手機的 2k 屏幕,我們的物理分辨率在變得原來越大。這樣就暴露出來一個問題,我們如果手機分辨率翻倍,我們的圖像不就要被縮小一倍,我們難道要在每個設備上就出個設計稿,每個設備的分辨不盡相同啊,其實你擔憂的問題,我們的喬幫主在很多年前就想到了。這就是我們的邏輯分辨率
1.2.4 邏輯分辨率(設備獨立像素)
如下圖所示,雖然設備物理分辨不同,但是他的這個邏輯分辨率卻都差不多,這就要感謝喬幫主了。
喬布斯在 iPhone4 的發布會上首次提出了 Retina Display(視網膜屏幕)的概念,在 iPhone4 使用的視網膜屏幕中,把 2x2 個像素當 1 個像素使用,這樣讓屏幕看起來更精致,但是元素的大小卻不會改變。從此以后高分辨率的設備,多了一個邏輯像素。這些設備邏輯像素的差別雖然不會跨度很大,但是仍然有點差別,于是便誕生了移動端頁面需要適配這個問題,既然邏輯像素由物理像素得來,那他們就會有一個像素比值。
1.2.5 設備像素比
設備像素比 device pixel ratio 簡稱 dpr,即物理像素和設備獨立像素的比值。為什么要知道設備像素比呢?因為這個像素比會產生一個非常經典的問題,1 像素邊框的問題。
1、1px 邊框問題
當我們 css 里寫的 1px 的時候,由于它是邏輯像素,導致我們的邏輯像素根據這個設備像素比(dpr)去映射到設備上就為 2px,或者 3px,由于每個設備的屏幕尺寸不一樣,就導致每個物理像素渲染出來的大小也不同(記得上面的知識點嗎,設備的像素大小是不固定的),這樣如果在尺寸比較大的設備上,1px 渲染出來的樣子相當的粗礦,這就是經典的一像素邊框問題。
2、如何解決?
核心思路,就是在 web 中,瀏覽器為我們提供了 window.devicePixelRatio 來幫助我們獲取 dpr。在 css 中,可以使用媒體查詢 min-device-pixel-ratio,區分 dpr:我們根據這個像素比,來算出他對應應該有的大小,但是暴露個非常大的兼容問題。
其中 Chrome 把 0.5px 四舍五入變成了 1px,而 firefox/safari 能夠畫出半個像素的邊,并且 Chrome 會把小于 0.5px 的當成 0,而 Firefox 會把不小于 0.55px 當成 1px,Safari 是把不小于 0.75px 當成 1px,進一步在手機上觀察 iOS 的 Chrome 會畫出 0.5px 的邊,而安卓(5.0)原生瀏覽器是不行的。所以直接設置 0.5px 不同瀏覽器的差異比較大,并且我們看到不同系統的不同瀏覽器對小數點的 px 有不同的處理。所以如果我們把單位設置成小數的 px 包括寬高等,其實不太可靠,因為不同瀏覽器表現不一樣。
至于其他解決一像素邊框問題網上有一堆答案,在這里我推薦一種非常好用,并且沒有副作用的解決方案。
transform: scale(0.5) 方案
div {
height:1px;
background:#000;
-webkit-transform: scaleY(0.5);
-webkit-transform-origin:0 0;
overflow: hidden;
}
css 根據設備像素比媒體查詢后的解決方案
/* 2倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 2.0) {
.border-bottom::after {
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
}
/* 3倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 3.0) {
.border-bottom::after {
-webkit-transform: scaleY(0.33);
transform: scaleY(0.33);
}
}
如此,完美的解決一像素看著粗的問題。
擴展補充
CSS 最新的規范中正在計劃通過標準的屬性實現一像素邊框,通過給border-width屬性添加hairline關鍵字屬性來實現,具體如下鏈接[1]。之所以叫hairline,是因為一像素邊框就跟頭發絲一樣。
練習使用方案時,也要多多關注最新發展喲。
如何適配?
2.1 viewport
視口(viewport)代表當前可見的計算機圖形區域。在 Web 瀏覽器術語中,通常與瀏覽器窗口相同,但不包括瀏覽器的 UI, 菜單欄等——即指你正在瀏覽的文檔的那一部分。
那么在移動端如何配置視口呢?簡單的一個 meta 標簽即可!
<meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;">
他們分別什么含義呢?
我們在移動端視口要想視覺效果和體驗好,那么我們的視口寬度必須無限接近理想視口。
理想視口:一般來講,這個視口其實不是真是存在的,它對設備來說是一個最理想布局視口尺寸,在用戶不進行手動縮放的情況下,可以將頁面理想地展示。那么所謂的理想寬度就是瀏覽器(屏幕)的寬度了。
于是上述的 meta 設置,就是我們的理想設置,他規定了我們的視口寬度為屏幕寬度,初始縮放比例為 1,就是初始時候我們的視覺視口就是理想視口!
其中 user-scalable 設置為 no 可以解決移動端點擊事件延遲問題(拓展)
2.2 適配方法
2.2.1 rem 適配
rem 是 CSS3 新增的一個相對單位,這個單位引起了廣泛關注。這個單位與 em 有什么區別呢?區別在于使用 rem 為元素設定字體大小時,仍然是相對大小,但相對的只是 HTML 根元素。這個單位可謂集相對大小和絕對大小的優點于一身,通過它既可以做到只修改根元素就成比例地調整所有字體大小,又可以避免字體大小逐層復合的連鎖反應。目前,除了 IE8 及更早版本外,所有瀏覽器均已支持 rem。對于不支持它的瀏覽器,應對方法也很簡單,就是多寫一個絕對單位的聲明。這些瀏覽器會忽略用 rem 設定的字體大小。
舉個例子:
//假設我給根元素的大小設置為14px
html{
font-size:14px
}
//那么我底下的p標簽如果想要也是14像素
p{
font-size:1rem
}
//如此即可
rem 的布局,不得不提 flexible,flexible 方案是阿里早期開源的一個移動端適配解決方案,引用 flexible 后,我們在頁面上統一使用 rem 來布局。
原理非常簡單:
// set 1rem = viewWidth / 10
function setRemUnit {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit;
rem 是相對于 html 節點的 font-size 來做計算的。所以在頁面初始話的時候給根元素設置一個 font-size,接下來的元素就根據 rem 來布局,這樣就可以保證在頁面大小變化時,布局可以自適應。
如此我們只需要給設計稿的 px 轉換成對應的 rem 單位即可。
當然,這個方案只是個過渡方案——為什么說是過渡方案?
因為當年 viewport 在低版本安卓設備上還有兼容問題,而 vw,vh 還沒能實現所有瀏覽器兼容,所以 flexible 方案用 rem 來模擬 vmin 來實現在不同設備等比縮放的“過度”方案,之所以說是過度方案,是因為這個他這個根據設備大小去判斷頁面的方案是根據屏幕大小去百分百還原設計稿,從而讓人看到的大小效果是一樣的,但是 蘋果 5 和蘋果 6p 雖然看到的設計稿還原是一樣的,但是他在一個合適距離上看到的效果能一樣嗎,本質上,用戶使用更大的屏幕,是想看到更多的內容,而不是更大的字。
so,這個用縮放來解決問題的方案是個過渡方案,注定被時代所淘汰。
2.2.2 vw,vh 布局
vh、vw 方案即將視覺視口寬度 window.innerWidth 和視覺視口高度 window.innerHeight 等分為 100 份。
vh 和 vw 方案和 rem 類似也是相當麻煩需要做單位轉化,而且 px 轉換成 vw 不一定能完全整除,因此有一定的像素差。
不過在工程化的今天,webpack 解析 css 的時候用 postcss-loader 有個 postcss-px-to-viewport 能自動實現 px 到 vw 的轉化。
{
loader: 'postcss-loader',
options: {
plugins: =>[
require('autoprefixer')({
browsers: ['last 5 versions']
}),
require('postcss-px-to-viewport')({
viewportWidth: 375, //視口寬度(數字)
viewportHeight: 1334, //視口高度(數字)
unitPrecision: 3, //設置的保留小數位數(數字)
viewportUnit: 'vw', //設置要轉換的單位(字符串)
selectorBlackList: ['.ignore', '.hairlines'], //不需要進行轉換的類名(數組)
minPixelValue: 1, //設置要替換的最小像素值(數字)
mediaQuery: false //允許在媒體查詢中轉換px(true/false)
})
]
}
2.2.3 px 為主,vx 和 vxxx(vw/vh/vmax/vmin)為輔,搭配一些 flex(推薦)
之所以推薦使用此種方案,是由于我們要去考慮用戶的需求,用戶之所以去買大屏手機,不是為了看到更大的字,而是為了看到更多的內容,這樣直接使用 px 是最明智的方案,使用 vw,rem 等布局手段無可厚非,但是,flex 這種彈性布局大行其道的今天,如果如果還用這種傳統的思維去想問題顯然是有兩個原因(個人認為 px 是最好的,可能有大佬,能用 vw,或者 rem 寫出精妙的布局,也說不準)。
為了偷懶,不愿意去做每個手機的適配
不愿意去學習新的布局方式,讓 flex 等先進的布局和你擦肩而過
2.3 移動端適配流程
在 head 設置 width=device-width 的 viewport‘
在 css 中使用 px
在適當的場景使用 flex 布局,或者配合 vw 進行自適應
在跨設備類型的時候(pc <-> 手機 <-> 平板)使用媒體查詢
在跨設備類型如果交互差異太大的情況,考慮分開項目開發
寫在最后
疫情期間有了跳槽的想法,問到移動端布局方面,雖然勉強能回答上來,但是總是支支吾吾,不是很了解,故而,發下宏愿,梳理移動端適配,幫助后來人后來者居上!
原文作者:好學習吧丶 整理:歪碼行空公眾號
原文鏈接:https://juejin.im/post/5e6caf55e51d4526ff026a71
聲明:本文為投稿,版權歸其個人所有。
作者:ChokCoco
https://www.cnblogs.com/coco1s/p/11463599.html
*請認真填寫需求信息,我們會在24小時內與您取得聯系。