手機淘寶的flexible設(shè)計與實現(xiàn)
手機淘寶從2014年中開始,全面推行flexible設(shè)計。什么叫flexible呢?其實flexible就是responsive的低端形態(tài)和基礎(chǔ)。對我們來說,最直觀的感受就是,在超寬屏幕上,網(wǎng)頁顯示不會兩邊留白。以前pc時代大家經(jīng)常講的流體布局,其實就是一種flexible design。只不過,流體的表述角度是實現(xiàn),flexible的表述角度是結(jié)果,為了跟高大上的responsive保持一致,我們這里使用了flexible這個說法。
討論方案之前,需要先了解三個關(guān)鍵概念:
單位英寸像素數(shù)(Pixel Per Inch,PPI):現(xiàn)實世界的一英寸內(nèi)像素數(shù),決定了屏幕的顯示質(zhì)量
設(shè)備像素比率(Device Pixel Ratio,DPR):物理像素與邏輯像素(px)的對應(yīng)關(guān)系
分辨率(Resolution):屏幕區(qū)域的寬高所占像素數(shù)
當我們決定不同屏幕的字體和尺寸的單位時,屏幕的這幾個參數(shù)非常重要。
場景1——Resolution適配
一張banner圖片,當你面對不同的屏幕時你希望它的行為是怎樣的?
在這個場景中,我們主要需要面對的是分辨率適配問題,考慮到多數(shù)網(wǎng)頁都是縱向滾動的,在不同的屏幕尺寸下,banner的行為應(yīng)該是總是鋪滿屏幕寬度以及總是保持寬高比。
最自然的思路是使用百分比寬度,但是假如使用百分比寬度,即width:100%,我們又有兩種思路來實現(xiàn)固定寬高比:一是利用img標簽的特性,只設(shè)寬度等圖片加載完,這種方法會導(dǎo)致大量的重排,并且非固定高度會導(dǎo)致懶加載等功能難以實現(xiàn),所以果斷放棄;二是使用before偽元素的margin撐開高度,這種方法是比較干凈的純css實現(xiàn),但是不具備任何復(fù)用性而且要求特定html結(jié)構(gòu),所以也只好放棄了。
于是,剩下最合適的辦法是使用其它相對單位,本來最合適的單位是vw,它的含義是視口寬度,但是這個單位存在嚴重的兼容問題,所以也只好放棄。
***我們只好配合js來做,硬算也是一條路,但是同樣不具備任何可復(fù)用性,最終我們選擇了rem,我們用js給html設(shè)置一個跟屏幕寬度成正比的font-size,然后把元素寬高都用rem作為單位。
這是我們目前的線上方案了,它是一個近乎Hack的用法,已知的問題包括:
某些Android機型會丟掉rem小數(shù)部分
占用了rem單位
不是純css方案
場景2——PPI適配
一段文字,當你面對不同的屏幕時你希望它的行為是怎樣的?
顯然,我們在iPhone3G和iPhone4的Retina屏下面,希望看到的文字尺寸是相同的,也就是說,我們不希望文字在Retina屏尺寸變小,此外,我們在大屏手機上,希望看到更多文字,以及,現(xiàn)在絕大多數(shù)的字體文件,是自帶一些點陣尺寸的,通常是16px和24px,所以我們不希望出現(xiàn)13px、15px這樣的奇葩尺寸。
這樣的特征決定了,場景1中的rem方案,不適合用到段落文字上。所以段落文字應(yīng)該使用px作為單位,考慮到Retina,我們利用media query來指定不同的字體,考慮到dpr判定的兼容性,我們用寬度替換來代替:
另一種場景,一些標題性文字,希望隨著屏幕寬而增大的,我們可以仍然使用rem作為單位。超過35px(個人直觀感受)的文字,已經(jīng)不用太考慮點陣信息了,靠字體的矢量信息也能渲染的很好。
場景3——DPR匹配
一個區(qū)塊,設(shè)計稿上有1像素邊框,當你面對不同的屏幕時你希望它的行為是怎樣的?
這個場景,需求很簡單,設(shè)計師希望在任何屏幕上這條線都是1物理像素。
好吧,當然這個問題的答案不是寫1px那么簡單。在retina屏下面,如果你寫了這樣的meta
你將永遠無法寫出1px寬度的東西,除此之外,inline的SVG等元素,也會按照邏輯像素來渲染,整個頁面的清晰度會打折。
所以,手機淘寶用JS來動態(tài)寫meta標簽,代碼類似這樣:
- var metaEl = doc.createElement('meta');
- var scale = isRetina ? 0.5:1;
- metaEl.setAttribute('name', 'viewport');
- metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
- if (docEl.firstElementChild) {
- document.documentElement.firstElementChild.appendChild(metaEl);
- } else {
- var wrap = doc.createElement('div');
- wrap.appendChild(metaEl);
- documen.write(wrap.innerHTML);
- }
結(jié)語
總的來說,手機淘寶的flexible方案是綜合運用rem和px兩種單位+js設(shè)置scale和html字體。
這些JS的內(nèi)容,可以在我們開源的庫ml中找到:https://github.com/amfe/lib.flexible。