眾所周知,移動(dòng)端適配的問(wèn)題一個(gè)長(zhǎng)期且艱巨的任務(wù)。市面上的適配方案也是五花八門,而且在不斷的更新。但是大部分的方案依舊在被使用,那就證明在不同需求下不同的方案是具有其實(shí)用性的。那本文將會(huì)簡(jiǎn)單的討論下筆者嘗試過(guò)并且運(yùn)用在生產(chǎn)環(huán)境中的幾種常見(jiàn)的方案。
直到目前位置,筆者嘗試過(guò)的移動(dòng)端的尺寸適配的方案也有好幾種,從最初的百分比單位,到rem,再到vh,vw,再到結(jié)合flex布局去使用這些適配單位,依舊在不斷的去優(yōu)化跟嘗試新的方案,也試著將這幾種融合起來(lái)去使用。運(yùn)用到生產(chǎn)環(huán)境后,取得了一些小成效,同時(shí)也發(fā)現(xiàn)了一些小缺點(diǎn)。下面將會(huì)分別對(duì)這幾個(gè)方案進(jìn)行簡(jiǎn)單的討論,分享下筆者使用時(shí)所遇到的一些問(wèn)題以及使用場(chǎng)景。
-
百分比單位(
%)
百分比單位是學(xué)習(xí)前端的時(shí)候最快接觸到的一個(gè)尺寸單位,%會(huì)基于父元素的尺寸進(jìn)行分配。如果父元素是一個(gè)200px * 400px的盒子的話,子元素設(shè)置width: 80%,則寬度會(huì)設(shè)置成160px,高度的話同理,百分比基于父元素的尺寸去做計(jì)算。對(duì)于父元素的尺寸的定好的情況,百分比是非常好用的。那么如果父元素沒(méi)有固定尺寸,是根據(jù)子元素的尺寸去撐大的情況呢?那么我們就會(huì)發(fā)現(xiàn)實(shí)際上顯示出來(lái)的高度是為0,而寬度不為0,不設(shè)置尺寸的父元素,其寬度會(huì)繼承其上一級(jí)的寬度,而高度則不會(huì),默認(rèn)為0。那么我們子元素所設(shè)置的百分比,寬度的值是正確的,高度為0。因?yàn)榘俜直然诟冈氐某叽缛ビ?jì)算,父元素寬度不為0,則可以計(jì)算出非零數(shù)值,而高度為0,計(jì)算出來(lái)只能為0。因此在父元素沒(méi)有固定尺寸,需要通過(guò)子元素去撐大的情況,高度使用百分比會(huì)得到0。所以高度使用百分比應(yīng)該謹(jǐn)慎使用。那么如果我們使用百分比去做適配不同尺寸的手機(jī)屏幕,保證在所有尺寸的手機(jī)屏幕上面都能以正確的比例去復(fù)刻UI設(shè)計(jì)的話,那么我們應(yīng)該在根元素那里設(shè)置好具體尺寸,那樣百分比才可以基于這個(gè)尺寸一級(jí)一級(jí)的計(jì)算下去。
-
rem單位
其實(shí)我們可以很直觀的發(fā)現(xiàn),如果我們的UI需求是可無(wú)限加載的滾動(dòng)列表的話,那么根元素上面就無(wú)法設(shè)置具體的高度,那么百分比在高度上面就會(huì)失效。那么我們只能設(shè)置具體高度給列表項(xiàng),但是這樣無(wú)法保證列表項(xiàng)在不同尺寸上面所保持的比例跟UI設(shè)計(jì)是一致。這就是百分比單位適配的一個(gè)很明顯的缺點(diǎn)。但是如果UI設(shè)計(jì)上不要求單項(xiàng)比例與UI保持一致的話,那么依舊可以使用百分比單位。
那么,既然百分比單位無(wú)法滿足我們的需求的話,我們就開(kāi)始尋找新的方案。這時(shí)候很容易就會(huì)發(fā)現(xiàn)到大家一直在說(shuō)的rem單位,rem也是web支持的眾多尺寸單位中的一個(gè),與其很像的是em,那么em跟rem有什么區(qū)別呢?em是基于父元素的font-size屬性,而rem則是基于根元素的font-size屬性,如果父元素設(shè)置了font-size: 50px,那么子元素的1em == 50px。而且如果子元素不設(shè)置font-size,會(huì)默認(rèn)繼承父元素的font-size,那么子元素的子元素的同樣滿足1em == 50px。那么可以直觀的理解得知,要是子元素設(shè)置了自己的font-size,那么就不再滿足上面的等式。所以如果當(dāng)父元素需要設(shè)置自己的font-size,而其子元素有想要基于祖父元素(父元素的父元素)去做em去設(shè)置尺寸的話,就會(huì)處于一個(gè)尷尬的情況。
那么rem又有什么不同呢?上面的時(shí)候我們提到了rem是基于根元素的font-size的大小,如果根元素設(shè)置了font-size: 50px,則我們?cè)谄淙魏巫釉刂谢蛘咔短椎亩嗉?jí)子元素中,使用width: 1rem,會(huì)得到width: 50px的反饋,而且無(wú)視于父元素的font-size是設(shè)置了什么值。即解決了em單位在上述提及的尷尬情況。但是需要注意的是,因?yàn)?code>font-size是可以逐級(jí)繼承傳遞下去的,所以最好在根元素下的第一級(jí)元素上將font-size設(shè)置回默認(rèn)值。之所以這么做的原因是,如果在逐級(jí)嵌套的過(guò)程中,出現(xiàn)了文本節(jié)點(diǎn),但是卻又沒(méi)有設(shè)置對(duì)應(yīng)的font-size,那么該文本節(jié)點(diǎn)的字體大小將會(huì)是根元素所設(shè)置的font-size大小,就會(huì)出現(xiàn)字體很大的情況。假如大部分文本節(jié)點(diǎn)的所需字體大小均為同一尺寸的話,那么在每一個(gè)文本節(jié)點(diǎn)的地方再重新設(shè)置font-size的值,其實(shí)會(huì)很不便。所以在根元素下的第一級(jí)的子元素設(shè)置好font-size的大小,那所有的文本節(jié)點(diǎn)就會(huì)繼承這個(gè)font-size的值,而不是根元素的。因此就可以不用在每個(gè)文本節(jié)點(diǎn)處去設(shè)置字體大小。
簡(jiǎn)單的介紹完了em & rem 這兩個(gè)單位,我們發(fā)現(xiàn)em & rem都有其適合的使用場(chǎng)景,但是相比之下rem可能更適合于去做整體的適配方案。那么我們應(yīng)該怎么去確定1rem的大小呢?在網(wǎng)上的資料中有多種確定的方案,其中主要就是兩種核心計(jì)算方式。
第一種就是,根據(jù)尺寸范圍去確定,也就是在移動(dòng)端中的多種尺寸中,劃分出多個(gè)范圍,在每個(gè)范圍中確定一個(gè)固定的數(shù)值。其具體實(shí)現(xiàn)就是結(jié)合媒體查詢,在不同范圍內(nèi)的屏幕尺寸設(shè)置一個(gè)固定的值,這樣去做適配。
另外一種就是,根據(jù)屏幕寬度跟設(shè)計(jì)稿寬度去做計(jì)算,得到一個(gè)數(shù)值。這樣計(jì)算在所有的尺寸均能得到一個(gè)對(duì)應(yīng)的唯一的值。其具體實(shí)現(xiàn)則是通過(guò)js去獲取到當(dāng)成設(shè)備的屏幕寬度然后去跟設(shè)計(jì)稿的寬度進(jìn)行轉(zhuǎn)換,計(jì)算得出一個(gè)值,然后在通過(guò)js動(dòng)態(tài)設(shè)置到根元素中。
這兩種的思路大體就是上述所講的,然而具體的轉(zhuǎn)換計(jì)算則不同公司有不同的想法。具體的轉(zhuǎn)換可以點(diǎn)擊這里。筆者自己采用方案是第二種,根據(jù)屏幕寬度跟設(shè)計(jì)稿寬度去轉(zhuǎn)換,那么下面會(huì)介紹下筆者是怎么轉(zhuǎn)換的。
筆者的轉(zhuǎn)換計(jì)算方式其實(shí)與網(wǎng)易手機(jī)端的計(jì)算方式是一致的,不過(guò)想出這個(gè)轉(zhuǎn)換計(jì)算方式并沒(méi)有像網(wǎng)易那樣理解了那么多,純粹只是這個(gè)樣算會(huì)很方便而已。因?yàn)槟玫绞值脑O(shè)計(jì)稿是750寬度的,一開(kāi)始試過(guò)750px轉(zhuǎn)成75rem的,那么實(shí)際設(shè)備的1rem大小就是通過(guò)使用屏幕寬度去除75得到1rem(實(shí)際設(shè)備),也就是1rem(實(shí)際設(shè)備) = w(屏幕寬度)/75,但是后來(lái)試了發(fā)現(xiàn)1rem=10px(設(shè)計(jì)稿)的這個(gè)計(jì)算,在chrome上面會(huì)無(wú)效,chrome最小只能到12px,而實(shí)際計(jì)算出來(lái)的 1rem遠(yuǎn)小于12px的,因?yàn)槭謾C(jī)的寬度大部分在360到480之間,這樣子計(jì)算出來(lái)的1rem(實(shí)際設(shè)備)肯定小于12px的。后來(lái)就換成1rem=100px(設(shè)計(jì)稿),也就是750px=7.5rem。之所有將1rem=10px或者1rem=100px,純粹只是因?yàn)檫@樣算,在切設(shè)計(jì)稿的尺寸的時(shí)候很方便,如果設(shè)計(jì)稿是有一個(gè)元素要130px * 80px,那么只需要設(shè)置1.3rem * 0.8rem。在計(jì)算上面只需要除100,可以直接在切圖的時(shí)候就能計(jì)算出來(lái),也不需要使用計(jì)算器去一個(gè)個(gè)去算,后來(lái)是這套方案使用了好一段時(shí)間后,才看到上面鏈接的那個(gè)博客介紹到網(wǎng)易手機(jī)端也是這么做的,而且考慮的遠(yuǎn)比我要想的多得多,才發(fā)現(xiàn)原來(lái)被我瞎貓碰上死耗子。
這套方案很長(zhǎng)一段時(shí)間都是我做移動(dòng)端適配的主選方案,直到我遇到了在ios的webview上面發(fā)現(xiàn)動(dòng)態(tài)加載網(wǎng)絡(luò)圖片使用rem的時(shí)候,圖片顯示不出來(lái),排查后發(fā)現(xiàn)是通過(guò)js接口調(diào)用后獲得的網(wǎng)絡(luò)圖片加載的時(shí)候就無(wú)法正確的顯示設(shè)置好的高度。這是我才開(kāi)始考慮有沒(méi)有更好的東西去結(jié)合rem去做得更好。因?yàn)橹白鑫⑿哦说膆5的時(shí)候,從來(lái)沒(méi)有遇到過(guò)使用rem會(huì)出現(xiàn)比例錯(cuò)誤的情況,在我看來(lái)rem仍然是在大部分界面上都能很好的做到適配的。不過(guò)在使用的過(guò)程中也發(fā)現(xiàn)了一些小問(wèn)題,因?yàn)?code>rem的單位計(jì)算是用屏幕寬度去處于7.5這個(gè)基數(shù),得到的1rem(實(shí)際設(shè)備)的值不能保證是整數(shù),所以設(shè)計(jì)稿上面一些小的間距,比如小于10px的那種,在css中我們會(huì)寫0.06rem這種情況,但是因?yàn)?code>1rem本身就不是整數(shù),所以在進(jìn)行浮點(diǎn)數(shù)計(jì)算的時(shí)候,會(huì)出現(xiàn)細(xì)微的差距。但是如果間距采用px去做,那么rem就會(huì)變的不準(zhǔn),尤其是在寬度上面,本身7.5rem就是一個(gè)屏幕寬度,但是加上px后就能計(jì)算出除去px后剩下的是多少個(gè)rem。
然后開(kāi)始全面擁抱彈性布局(flex),在使用flex布局后,發(fā)現(xiàn)結(jié)合flex跟rem可以做的更好。因?yàn)槿绻g距采用px,某些元素采用rem,那么剩下的可以直接flex拉伸鋪滿,就不用擔(dān)心除去px后還剩多少個(gè)rem的問(wèn)題。在移動(dòng)端上面基本上都是兼容了flex這個(gè)特性,所以可以直接使用,不過(guò)需要加下對(duì)應(yīng)的內(nèi)核前綴即可。flex的使用很大程度優(yōu)化了在布局上面的編寫。
后面又發(fā)現(xiàn)了vw & vh這一對(duì),vw其實(shí)就是基于window.innerWidth,vh就是基于window.innerHeight。 100vw就等于屏幕可視范圍的寬度,100vh就等于屏幕可視范圍的高度,在移動(dòng)端的話就是webview的尺寸。而且發(fā)現(xiàn)到vw就很像將750px=100rem(設(shè)計(jì)稿)的這種情況,這種情況下的1rem的大小就跟1vw的大小是一致的,所以vw的使用上基本與rem的使用相似。而且一屏的寬度等于100vw也很像百分比,但是vw卻是基于屏幕寬度的,而不會(huì)想百分比那樣基于父元素,所以在橫向的適配上做到很不錯(cuò)的效果。但是vh呢?因?yàn)橛袝r(shí)候需要做一個(gè)H5界面是要一屏的寬高,這時(shí)候vh就發(fā)揮出它的作用了,正常情況,100vh就是一屏的高度。但是在使用vh的過(guò)程遇到過(guò)一個(gè)問(wèn)題,就像一個(gè)一屏的H5注冊(cè)頁(yè),里面有input標(biāo)簽,需要用戶填入信息。將背景圖的高度設(shè)置為100vh,當(dāng)輸入框聚焦后,手機(jī)鍵盤彈起,這時(shí)候背景圖就會(huì)被壓縮,因?yàn)槭謾C(jī)鍵盤彈起后,屏幕的可視高度就是一屏的高度減去手機(jī)鍵盤的高度,因此100vh就會(huì)變成這個(gè)高度而不再是一屏的高度。這個(gè)問(wèn)題就是使用vw跟vh的時(shí)候遇到過(guò)的唯一問(wèn)題。暫時(shí)還沒(méi)想出好的解決方法。如果不會(huì)出現(xiàn)鍵盤彈出的情況且又需要一屏的高度的時(shí)候,很推薦使用vh。而vw則暫時(shí)還沒(méi)遇到過(guò)問(wèn)題。
總結(jié)
適配方面嘗試過(guò)好幾種方法,每種方法都有其優(yōu)點(diǎn)跟缺點(diǎn),但是根據(jù)使用場(chǎng)景去選擇跟結(jié)合來(lái)使用,就可以做到相當(dāng)不錯(cuò)的適配效果了。
注:后續(xù)會(huì)添加一些代碼圖片跟實(shí)際效果的圖片