在進(jìn)入正文之前,我們需要先了解一些viewport相關(guān)的概念。
viewport
一般來(lái)說(shuō),我們?cè)诖a的最開(kāi)始會(huì)寫(xiě)上這么一句
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
移動(dòng)端瀏覽器可以在一個(gè)比屏幕更寬的虛擬”窗口“中渲染頁(yè)面,從而無(wú)需將所有頁(yè)面都?jí)嚎s進(jìn)小屏幕里(那樣會(huì)把很多沒(méi)有針對(duì)移動(dòng)端進(jìn)行優(yōu)化的站點(diǎn)打亂)。用戶(hù)可以通過(guò)平移和縮放來(lái)瀏覽頁(yè)面的不同區(qū)域。比如我們把一個(gè)頁(yè)面的寬度寫(xiě)成了10000px,那么我們照樣可以通過(guò)平移縮放來(lái)看到這個(gè)頁(yè)面,當(dāng)然這是不可取的,所以我們就需要以上代碼,將viewport的width設(shè)置為設(shè)備的寬度,縮放的倍數(shù)是1,且不讓用戶(hù)進(jìn)行縮放(這個(gè)可以根據(jù)實(shí)際業(yè)務(wù)場(chǎng)景來(lái)設(shè)置)。經(jīng)過(guò)以上設(shè)置,我們就可以開(kāi)始進(jìn)入愉快寫(xiě)代碼的第一步了~
- 物理像素DP(device pixels)和css像素
物理像素一般在設(shè)備出廠的時(shí)候就被設(shè)置好了,也叫設(shè)備像素,單 位是pt(point),pt是一個(gè)物理單位,指的是組成顯示器屏幕的絕對(duì)長(zhǎng)度。我們買(mǎi)電視機(jī)的時(shí)候,會(huì)說(shuō),我買(mǎi)個(gè)70寸的電視機(jī)。
1英寸=72pt
px(pixel)是一個(gè)虛擬的單位,也是一個(gè)相對(duì)的單位。根據(jù)《CSS權(quán)威指南》的像素理論中的解釋?zhuān)?/p>
顯示器上的小色框是像素
CSS2.1 建議采用96ppi(pixels per inch),這是Windows機(jī)器常用的度量。Mac系統(tǒng)下用的是72ppi。
摘錄來(lái)自: Eric Meyer. “CSS權(quán)威指南第三版”。 iBooks.
1px=1/ppi英寸
通過(guò)inch就可以連接器pt和px的關(guān)系。

- 設(shè)備獨(dú)立像素DIP(Device independent Pixel)
設(shè)備獨(dú)立像素,也稱(chēng)為邏輯像素,也叫css像素。我們可以通過(guò)screen.width或者screen.height輸出。比如iPhone的css像素就是375px*667px,iPhone 6 則采用了750×1334分辨率的屏幕,PPI值為326。

物理像素和css像素的計(jì)算公式是:
DPR = 物理像素/CSS像素
css reset樣式重置
為什么要進(jìn)行css reset操作?
css reset主要是因?yàn)閔tml標(biāo)簽在瀏覽器中都有各自的默認(rèn)樣式。
比如: p 標(biāo)簽有上下邊距,strong標(biāo)簽有字體加粗樣式,em標(biāo)簽有字體傾斜樣式。不同瀏覽器的默認(rèn)樣式之間也會(huì)有差別,例如ul默認(rèn)帶有縮進(jìn)的樣式,在IE 下,它的縮進(jìn)是通過(guò)margin實(shí)現(xiàn)的,而Firefox下,它的縮進(jìn)是由padding實(shí)現(xiàn)的。在切換頁(yè)面的時(shí)候,瀏覽器的默認(rèn)樣式往往會(huì)給我們帶來(lái)麻煩,影響開(kāi)發(fā)效率。所以解決的方法就是一開(kāi)始就將瀏覽器的默認(rèn)樣式全部去掉,更準(zhǔn)確說(shuō)就是通過(guò)重新定義標(biāo)簽樣式。
常用的適配方案
- flex
<div class="content">
<div class="con">
<div class="a"></div>
</div>
<div class="con">
<div class="a"></div>
</div>
<div class="con">
<div class="a"></div>
</div>
</div>
.content{
display: flex;
}
.con{
display: flex;
justify-content:center;
align-items:center;
flex:1;
height: 300px;
background: #999;
color:#fff;
border:1px solid #000;
}
.a{
width: 30px;
height: 30px;
background: #f00;
}

-
媒體查詢(xún)media query
一個(gè)媒體查詢(xún)由一個(gè)可選的媒體類(lèi)型和零個(gè)或多個(gè)使用媒體功能的限制了樣式表范圍的表達(dá)式組成,例如寬度、高度和顏色。媒體查詢(xún),添加自CSS3,允許內(nèi)容的呈現(xiàn)針對(duì)一個(gè)特定范圍的輸出設(shè)備而進(jìn)行裁剪,而不必改變內(nèi)容本身。
我們把上面flex的css換掉其中a的樣式,來(lái)看一下media query的作用。
.a{
background:#f00;
}
@media (max-width: 800px) {
.a {
width:50px;
height:50px;
font-size:12px;
color:#2e9900;
}
}
@media (min-width:801px) and (max-width:1000px) {
.a {
width:200px;
height:200px;
font-size:20px;
color:blue;
}
}
@media (min-width: 1001px) {
.a {
width:450px;
height:450px;
font-size:40px;
}
}
- transform-scale
其實(shí)我自己覺(jué)得transform-scale是一個(gè)不錯(cuò)的適配方式,簡(jiǎn)單明了,而且暫時(shí)也沒(méi)有遇到過(guò)什么適配方面的大坑,相對(duì)而言還比較精確。
處理方式很簡(jiǎn)單,樣式完全按照設(shè)計(jì)稿的px來(lái)寫(xiě)。然后在js中計(jì)算當(dāng)前頁(yè)面的寬度與設(shè)計(jì)稿的寬度的比,直接對(duì)body進(jìn)行transform-scale的縮放。
如果非要說(shuō)有什么問(wèn)題的話,那就是如果需要考慮橫屏受眾,那就得針對(duì)一些橫屏的狀態(tài)做一些特殊處理。還有就是transform-scale縮小沒(méi)有問(wèn)題,放大的話,會(huì)導(dǎo)致頁(yè)面模糊。
這個(gè)方案就看自己業(yè)務(wù)需求來(lái)選擇了。
- rem
rem算是一個(gè)使用很廣泛的適配方案了。
實(shí)現(xiàn)的方式就是給html根節(jié)點(diǎn)設(shè)置一個(gè)font-size,作為頁(yè)面計(jì)算的基準(zhǔn)。一般這個(gè)基準(zhǔn)會(huì)根據(jù)設(shè)計(jì)稿來(lái)定。
假設(shè)設(shè)計(jì)稿的寬度是750px,那最初我們可以將font-size設(shè)為75px,頁(yè)面寬度就是10rem。
然后在頁(yè)面中用js動(dòng)態(tài)獲取一下當(dāng)前頁(yè)面寬度,計(jì)算出頁(yè)面寬度與750px的比例,獲取當(dāng)前頁(yè)面根節(jié)點(diǎn)上應(yīng)該帶上多大的font-size,假設(shè)當(dāng)前頁(yè)面寬度是375px,那么根節(jié)點(diǎn)上帶上的font-size應(yīng)該是37.5px。
這個(gè)方案是一個(gè)與dpr無(wú)關(guān)的方案,就是說(shuō)完全依賴(lài)的是設(shè)備獨(dú)立像素而不是設(shè)備的物理像素,這樣就會(huì)存在一個(gè)問(wèn)題,在dpr較大的高清屏上,可能導(dǎo)致模糊。
當(dāng)然,我們也是可以加上dpr來(lái)計(jì)算根節(jié)點(diǎn)的font-size的。
- flexible.js
flexible是手淘的一套解決方案,這是一套dpr相關(guān)的解決方案。
實(shí)現(xiàn)的原理是:
flexible將頁(yè)面分成100份,即100a,10a=1rem。比如,頁(yè)面寬度為750px,那么一份為75px,1rem=75px。
通過(guò)js動(dòng)態(tài)獲取當(dāng)前頁(yè)面的dpr和設(shè)備獨(dú)立像素。拿iPhone 8 plus舉例,得到dpr是3,設(shè)備獨(dú)立像素是414px。那么html上寫(xiě)的font-size的大小是這么計(jì)算的:
dpr * 414 / 10
最終得到的font-size值是124.2px,然后在meta的initial-scale里會(huì)根據(jù)dpr再進(jìn)行一下頁(yè)面縮放,這就可以保證頁(yè)面的精細(xì)程度。
但是如果在meta viewport中手動(dòng)設(shè)置了initial-scale,那么不管js獲取到的dpr是多少,都會(huì)強(qiáng)制認(rèn)為dpr是手動(dòng)設(shè)置的值;
手淘處理文字的方式,并沒(méi)有采用rem,因?yàn)閞em計(jì)算出的小數(shù),在對(duì)小數(shù)敏感度不一樣的機(jī)型上會(huì)導(dǎo)致展示誤差,且小于12px的文字并不會(huì)正常被渲染。也會(huì)導(dǎo)致一些line-height的問(wèn)題。
- vm
隨著前端技術(shù)的演進(jìn),vw這個(gè)單位又走上了歷史的舞臺(tái)。
在CSS Values and Units Module Level 3中和Viewport相關(guān)的單位有四個(gè),分別為vw、vh、vmin和vmax。
- `vw`:是Viewport's width的簡(jiǎn)寫(xiě),`1vw`等于`window.innerWidth`的`1%`
- `vh`:和`vw`類(lèi)似,是Viewport's height的簡(jiǎn)寫(xiě),`1vh`等于`window.innerHeihgt`的`1%`
- `vmin`:`vmin`的值是當(dāng)前`vw`和`vh`中較小的值
- `vmax`:`vmax`的值是當(dāng)前`vw`和`vh`中較大的值
常見(jiàn)的屏幕適配問(wèn)題和解決方案
- line-height
一般客戶(hù)端的webview會(huì)有一個(gè)默認(rèn)的line-height,大概會(huì)是在22px的樣子。我們有可能會(huì)遇到一個(gè)問(wèn)題就是,在我們?cè)O(shè)置好一個(gè)line-height之后,但是渲染出來(lái)的文字相對(duì)于我們預(yù)想的是偏上的。導(dǎo)致這個(gè)問(wèn)題的原因是,我們?cè)O(shè)置的line-height是小于webview預(yù)設(shè)的最小line-height的,這樣我們?cè)O(shè)置的那個(gè)值會(huì)被忽略。
解決的方案是:
盡量保證font-size是大于12px的,將line-height設(shè)置為1,使用上下padding使文字居中。在一些必須需要font-size小于12px的地方,可以先放font-size大于12px,再用transform:scale去縮小。
但是這一點(diǎn)從實(shí)際經(jīng)驗(yàn)來(lái)看,我更偏愛(ài)手淘的方案,根據(jù)dpr或者mediaquery設(shè)置一個(gè)固定的字體,這就需要和設(shè)計(jì)同學(xué)定下一個(gè)設(shè)計(jì)規(guī)范。 - 中英文的字號(hào)一樣,但是實(shí)際高度卻不一樣
文字的大小,取決于設(shè)計(jì)文字的人,就像我們前幾天遇到的英文比中文同字號(hào)小的問(wèn)題,如果光靠上下的padding來(lái)寫(xiě)的話,那就可能會(huì)出現(xiàn)純英文或者數(shù)字那塊會(huì)比較矮。 - 1像素問(wèn)題
產(chǎn)生原因:因?yàn)閐pr高的設(shè)備下,寫(xiě)的1像素,實(shí)際是比1的dpr倍。
解決方案:1)如果整體縮放頁(yè)面的話,1像素的線也會(huì)被一起縮放
2)可以將線條所在元素用transform-scale縮小 - 如果設(shè)計(jì)師在設(shè)計(jì)稿上使用的文字很細(xì),比如華文細(xì)黑這樣的,那前端為了適配更完美,可以用一個(gè)樣式-webkit-font-smoothing: antialiased;
- 在移動(dòng)端我們寫(xiě)了overflow:scroll;之后,滑動(dòng)可能并不能像我們預(yù)料中的那么順暢,可以使用樣式-webkit-overflow-scrolling: touch;
- 如果input或button的有怪異的默認(rèn)樣式,嘗試appearance:none;(-webkit-appearance:none;)