使用Flexible实现手淘H5页面的终端适配
轉載自:使用Flexible實現手淘H5頁面的終端適配
更多參考:移動端高清、多屏適配方案? ??移動端應該如何動態設置字體大小?
曾幾何時為了兼容IE低版本瀏覽器而頭痛,以為到Mobile時代可以跟這些麻煩說拜拜。可沒想到到了移動時代,為了處理各終端的適配而亂了手腳。對于混跡各社區的偶,時常發現大家拿手機淘寶的H5頁面做討論——?手淘的H5頁面是如何實現多終端的適配??
那么趁此?Amfe阿里無線前端團隊雙11技術連載?之際,用一個實戰案例來告訴大家,手淘的H5頁面是如何實現多終端適配的,希望這篇文章對大家在Mobile的世界中能過得更輕松。
目標
拿一個雙11的Mobile頁面來做案例,比如你實現一個類似下圖的一個H5頁面:
目標很清晰,就是做一個這樣的H5頁面。
痛點
雖然H5的頁面與PC的Web頁面相比簡單了不少,但讓我們頭痛的事情是要想盡辦法讓頁面能適配眾多不同的終端設備。看看下圖你就會知道,這是多么痛苦的一件事情:
點擊?這里?查看更多終端設備的參數。
再來看看手淘H5要適配的終端設備數據:
看到這些數據,是否死的心都有了,或者說為此捏了一把汗出來。
手淘團隊適配協作模式
早期移動端開發,對于終端設備適配問題只屬于Android系列,只不過很多設計師常常忽略Android適配問題,只出一套iOS平臺設計稿。但隨著iPhone6,iPhone6+的出現,從此終端適配問題不再是Android系列了,也從這個時候讓移動端適配全面進入到“雜屏”時代。
上圖來自于?paintcodeapp.com
為了應對這多么的終端設備,設計師和前端開發之間又應該采用什么協作模式?或許大家對此也非常感興趣。
而整個手淘設計師和前端開發的適配協作基本思路是:
- 選擇一種尺寸作為設計和開發基準
- 定義一套適配規則,自動適配剩下的兩種尺寸(其實不僅這兩種,你懂的)
- 特殊適配效果給出設計效果
還是上一張圖吧,因為一圖勝過千言萬語:
在此也不做更多的闡述。在手淘的設計師和前端開發協作過程中:?手淘設計師常選擇iPhone6作為基準設計尺寸,交付給前端的設計尺寸是按?750px * 1334px?為準(高度會隨著內容多少而改變)。前端開發人員通過一套適配規則自動適配到其他的尺寸。
根據上面所說的,設計師給我們的設計圖是一個?750px * 1600px?的頁面:
前端開發完成終端適配方案
拿到設計師給的設計圖之后,剩下的事情是前端開發人員的事了。而手淘經過多年的摸索和實戰,總結了一套移動端適配的方案——?flexible方案?。
這種方案具體在實際開發中如何使用,暫時先賣個關子,在繼續詳細的開發實施之前,我們要先了解一些基本概念。
一些基本概念
在進行具體實戰之前,首先得了解下面這些基本概念(術語):
視窗 viewport
簡單的理解,viewport是嚴格等于瀏覽器的窗口。在桌面瀏覽器中,viewport就是瀏覽器窗口的寬度高度。但在移動端設備上就有點復雜。
移動端的viewport太窄,為了能更好為CSS布局服務,所以提供了兩個viewport:虛擬的viewportvisualviewport和布局的viewportlayoutviewport。
George Cummins在Stack Overflow上?對這兩個基本概念做了詳細的解釋?。
而事實上viewport是一個很復雜的知識點,上面的簡單描述可能無法幫助你更好的理解viewport,而你又想對此做更深的了解,可以閱讀PPK寫的相關教程。
物理像素(physical pixel)
物理像素又被稱為設備像素,他是顯示設備中一個最微小的物理部件。每個像素可以根據操作系統設置自己的顏色和亮度。正是這些設備像素的微小距離欺騙了我們肉眼看到的圖像效果。
設備獨立像素(density-independent pixel)
設備獨立像素也稱為密度無關像素,可以認為是計算機坐標系統中的一個點,這個點代表一個可以由程序使用的虛擬像素(比如說CSS像素),然后由相關系統轉換為物理像素。
CSS像素
CSS像素是一個抽像的單位,主要使用在瀏覽器上,用來精確度量Web頁面上的內容。一般情況之下,CSS像素稱為與設備無關的像素(device-independent pixel),簡稱DIPs。
屏幕密度
屏幕密度是指一個設備表面上存在的像素數量,它通常以每英寸有多少像素來計算(PPI)。
設備像素比(device pixel ratio)
設備像素比簡稱為dpr,其定義了物理像素和設備獨立像素的對應關系。它的值可以按下面的公式計算得到:
設備像素比 = 物理像素 / 設備獨立像素在JavaScript中,可以通過?window.devicePixelRatio?獲取到當前設備的dpr。而在CSS中,可以通過?-webkit-device-pixel-ratio?,?-webkit-min-device-pixel-ratio和?-webkit-max-device-pixel-ratio?進行媒體查詢,對不同dpr的設備,做一些樣式適配(這里只針對webkit內核的瀏覽器和webview)。
dip或dp,(device independent pixels,設備獨立像素)與屏幕密度有關。dip可以用來輔助區分視網膜設備還是非視網膜設備。
縮合上述的幾個概念,用一張圖來解釋:
眾所周知,iPhone6的設備寬度和高度為?375pt * 667pt?,可以理解為設備的獨立像素;而其dpr為?2?,根據上面公式,我們可以很輕松得知其物理像素為?750pt * 1334pt?。
如下圖所示,某元素的CSS樣式:
width: 2px; height: 2px;在不同的屏幕上,CSS像素所呈現的物理尺寸是一致的,而不同的是CSS像素所對應的物理像素具數是不一致的。在普通屏幕下?1?個CSS像素對應?1?個物理像素,而在Retina屏幕下,?1?個CSS像素對應的卻是?4?個物理像素。
有關于更多的介紹可以點擊這里詳細了解。
看到這里,你能感覺到,在移動端時代屏幕適配除了Layout之外,還要考慮到圖片的適配,因為其直接影響到頁面顯示質量,對于如何實現圖片適配,再此不做過多詳細闡述。這里盜用了?@南宮瑞揚?根據?mir.aculo.us?翻譯的一張信息圖:
meta標簽
<meta>?標簽有很多種,而這里要著重說的是viewport的?meta?標簽,其主要用來告訴瀏覽器如何規范的渲染Web頁面,而你則需要告訴它視窗有多大。在開發移動端頁面,我們需要設置?meta?標簽如下:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">代碼以顯示網頁的屏幕寬度定義了視窗寬度。網頁的比例和最大比例被設置為100%。
留個懸念,因為后面的解決方案中需要重度依賴?meta?標簽。
CSS單位rem
在?W3C?規范中是這樣描述?rem?的:
font size of the root element.
簡單的理解,?rem?就是相對于根元素?<html>?的?font-size?來做計算。而我們的方案中使用?rem?單位,是能輕易的根據?<html>?的?font-size?計算出元素的盒模型大小。而這個特色對我們來說是特別的有益處。
前端實現方案
了解了前面一些相關概念之后,接下來我們來看實際解決方案。在整個手淘團隊,我們有一個名叫?lib-flexible?的庫,而這個庫就是用來解決H5頁面終端適配的。
lib-flexible是什么?
lib-flexible?是一個制作H5適配的開源庫,可以?點擊這里?下載相關文件,獲取需要的JavaScript和CSS文件。
當然你可以直接使用阿里CDN:
<script src="http://g.tbcdn.cn/mtb/lib-flexible/{{version}}/??flexible_css.js,flexible.js"></script>將代碼中的?{{version}}?換成對應的版本號?0.3.4?。
使用方法
lib-flexible?庫的使用方法非常的簡單,只需要在Web頁面的?<head></head>?中添加對應的?flexible_css.js,flexible.js?文件:
第一種方法是將文件下載到你的項目中,然后通過相對路徑添加:
<script src="build/flexible_css.debug.js"></script> <script src="build/flexible.debug.js"></script>或者直接加載阿里CDN的文件:
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script>另外強烈建議對JS做?內斂處理?,在所有資源加載之前執行這個JS。執行這個JS后,會在?<html>?元素上增加一個?data-dpr?屬性,以及一個?font-size?樣式。JS會根據不同的設備添加不同的?data-dpr?值,比如說?2?或者?3?,同時會給?html?加上對應的?font-size?的值,比如說?75px?。
如此一來,頁面中的元素,都可以通過?rem?單位來設置。他們會根據?html?元素的?font-size?值做相應的計算,從而實現屏幕的適配效果。
除此之外,在引入?lib-flexible?需要執行的JS之前,可以手動設置?meta?來控制?dpr?值,如:
<meta name="flexible" content="initial-dpr=2" />其中?initial-dpr?會把?dpr?強制設置為給定的值。如果手動設置了?dpr?之后,不管設備是多少的?dpr?,都會強制認為其?dpr?是你設置的值。在此不建議手動強制設置?dpr?,因為在Flexible中,只對iOS設備進行?dpr?的判斷,對于Android系列,始終認為其?dpr?為?1?。
if (!dpr && !scale) {var isAndroid = win.navigator.appVersion.match(/android/gi);var isIPhone = win.navigator.appVersion.match(/iphone/gi);var devicePixelRatio = win.devicePixelRatio;if (isIPhone) {// iOS下,對于2和3的屏,用2倍的方案,其余的用1倍方案if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { dpr = 3;} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){dpr = 2;} else {dpr = 1;}} else {// 其他設備下,仍舊使用1倍的方案dpr = 1;}scale = 1 / dpr; }flexible的實質
flexible?實際上就是能過JS來動態改寫?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); }事實上他做了這幾樣事情:
- 動態改寫?<meta>?標簽
- 給?<html>?元素添加?data-dpr?屬性,并且動態改寫?data-dpr?的值
- 給?<html>?元素添加?font-size?屬性,并且動態改寫?font-size?的值
案例實戰
了解Flexible相關的知識之后,咱們回到文章開頭。我們的目標是制作一個適配各終端的H5頁面。別的不多說,動手才能豐衣足食。
創建HTML模板
<html lang="en"><head><meta charset="utf-8"><meta content="yes" name="apple-mobile-web-app-capable"><meta content="yes" name="apple-touch-fullscreen"><meta content="telephone=no,email=no" name="format-detection"><script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script><link rel="apple-touch-icon" href="favicon.png"><link rel="Shortcut Icon" href="favicon.png" type="image/x-icon"><title>再來一波</title></head><body><!-- 頁面結構寫在這里 --></body> </html>正如前面所介紹的一樣,首先加載了Flexible所需的配置:
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script>這個時候可以根據設計的圖需求,在HTML文檔的?<body></body>?中添加對應的HTML結構,比如:
<div class="item-section" data-repeat="sections"><div class="item-section_header"><h2><img src="{brannerImag}" alt=""></h2></div><ul><li data-repeat="items" class="flag" role="link" href="{itemLink}"><a class="figure flag-item" href="{itemLink}"><img src="{imgSrc}" alt=""></a><div class="figcaption flag-item"><div class="flag-title"><a href="{itemLink}" title="">{poductName}</a></div><div class="flag-price"><span>雙11價</span><strong>¥{price}</strong><small>({preferential})</small></div><div class="flag-type">{activityType}</div><a class="flag-btn" href="{shopLink}">{activeName}</a></div></li></ul> </div>這僅是一個示例文檔,大家可以根據自己風格寫模板?。
為了能更好的測試頁面,給其配置一點假數據:
//define data var pageData = {sections:[{"brannerImag":"http://xxx.cdn.com/B1PNLZKXXXXXaTXXXXXXXXXXXX-750-481.jpg",items:[{"itemLink": "##","imgSrc": "https://placeimg.com/350/350/people/grayscale","poductName":"Carter's1年式灰色長袖連體衣包腳爬服全棉鯨魚男嬰兒童裝115G093","price": "299.06","preferential": "滿400減100","activityType": "1小時內熱賣5885件","shopLink":"##","activeName": "馬上搶!"}....}]}] }接下來的工作就是美化工作了。在寫具體樣式之前,有幾個點需要先了解一下。
把視覺稿中的?px?轉換成?rem
讀到這里,大家應該都知道,我們接下來要做的事情,就是如何把視覺稿中的?px轉換成?rem?。在此花點時間解釋一下。
首先,目前日常工作當中,視覺設計師給到前端開發人員手中的視覺稿尺寸一般是基于?640px?、?750px?以及?1125px?寬度為準。甚至為什么?大家應該懂的(考慮Retina屏)。
正如文章開頭顯示的示例設計稿,他就是一張以?750px?為基礎設計的。那么問題來了,我們如何將設計稿中的各元素的?px?轉換成?rem?。
我廠的視覺設計師想得還是很周到的,會幫你把相關的信息在視覺稿上標注出來?。
目前Flexible會將視覺稿分成?100份?(主要為了以后能更好的兼容?vh?和?vw?),而每一份被稱為一個單位?a?。同時?1rem?單位被認定為?10a?。針對我們這份視覺稿可以計算出:
1a = 7.5px 1rem = 75px那么我們這個示例的稿子就分成了?10a?,也就是整個寬度為?10rem?,?<html>?對應的?font-size?為?75px?:
這樣一來,對于視覺稿上的元素尺寸換算,只需要原始的?px值?除以?rem基準值?即可。例如此例視覺稿中的圖片,其尺寸是?176px * 176px?,轉換成為?2.346667rem * 2.346667rem?。
如何快速計算
在實際生產當中,如果每一次計算?px?轉換?rem?,或許會覺得非常麻煩,或許直接影響大家平時的開發效率。為了能讓大家更快進行轉換,我們團隊內的同學各施所長,為?px?轉換?rem?寫了各式各樣的小工具。
CSSREM
CSSREM?是一個CSS的?px?值轉?rem?值的Sublime Text3自動完成插件。這個插件是由?@正霖?編寫。先來看看插件的效果:
有關于CSSREM如何安裝、配置教程可以?點擊這里查閱?。
CSS處理器
除了使用編輯器的插件之外,還可以使用CSS的處理器來幫助大家處理。比如說Sass、LESS以及PostCSS這樣的處理器。我們簡單來看兩個示例。
Sass
使用Sass的同學,可以使用Sass的函數、混合宏這些功能來實現:
@function px2em($px, $base-font-size: 16px) {@if (unitless($px)) {@warn "Assuming #{$px} to be in pixels, attempting to convert it into pixels for you";@return px2em($px + 0px); // That may fail.} @else if (unit($px) == em) {@return $px;}@return ($px / $base-font-size) * 1em; }除了使用Sass函數外,還可以使用Sass的混合宏:
@mixin px2rem($property,$px-values,$baseline-px:16px,$support-for-ie:false){//Conver the baseline into rems$baseline-rem: $baseline-px / 1rem * 1;//Print the first line in pixel values@if $support-for-ie {#{$property}: $px-values;}//if there is only one (numeric) value, return the property/value line for it.@if type-of($px-values) == "number"{#{$property}: $px-values / $baseline-rem;}@else {//Create an empty list that we can dump values into$rem-values:();@each $value in $px-values{// If the value is zero or not a number, return it@if $value == 0 or type-of($value) != "number"{$rem-values: append($rem-values, $value / $baseline-rem);}}// Return the property and its list of converted values#{$property}: $rem-values;} }有關于更多的介紹,可以點擊這里進行了解。
PostCSS(px2rem)
除了Sass這樣的CSS處理器這外,我們團隊的@頌奇同學還開發了一款?npm?的工具?px2rem?。安裝好?px2rem?之后,可以在項目中直接使用。也可以使用PostCSS。使用PostCSS插件?postcss-px2rem?:
var gulp = require('gulp'); var postcss = require('gulp-postcss'); var px2rem = require('postcss-px2rem'); gulp.task('default', function() {var processors = [px2rem({remUnit: 75})];return gulp.src('./src/*.css').pipe(postcss(processors)).pipe(gulp.dest('./dest')); });除了在Gulp中配置外,還可以使用其他的配置方式,詳細的介紹可以?點擊這里?進行了解。
配置完成之后,在實際使用時,你只要像下面這樣使用:
.selector {width: 150px;height: 64px; /*px*/font-size: 28px; /*px*/border: 1px solid #ddd; /*no*/ }px2rem?處理之后將會變成:
.selector {width: 2rem;border: 1px solid #ddd; } [data-dpr="1"] .selector {height: 32px;font-size: 14px; } [data-dpr="2"] .selector {height: 64px;font-size: 28px; } [data-dpr="3"] .selector {height: 96px;font-size: 42px; }在整個開發中有了這些工具之后,完全不用擔心?px?值轉?rem?值影響開發效率。
字號不使用?rem
前面大家都見證了如何使用?rem?來完成H5適配。那么文本又將如何處理適配。是不是也通過?rem?來做自動適配。
顯然,我們在iPhone3G和iPhone4的Retina屏下面,希望看到的文本字號是相同的。也就是說,我們?不希望文本在Retina屏幕下變小?,另外,我們?希望在大屏手機上看到更多文本?,以及,現在絕大多數的字體文件都自帶一些點陣尺寸,通常是?16px?和?24px?,所以我們?不希望出現?13px?和?15px?這樣的奇葩尺寸?。
如此一來,就決定了在制作H5的頁面中,?rem?并不適合用到段落文本上。所以在Flexible整個適配方案中,考慮文本還是使用?px?作為單位。只不過使用?[data-dpr]?屬性來區分不同?dpr?下的文本字號大小。
div {width: 1rem; height: 0.4rem;font-size: 12px; // 默認寫上dpr為1的fontSize } [data-dpr="2"] div {font-size: 24px; } [data-dpr="3"] div {font-size: 36px; }為了能更好的利于開發,在實際開發中,我們可以定制一個?font-dpr()?這樣的Sass混合宏:
@mixin font-dpr($font-size){font-size: $font-size;[data-dpr="2"] & {font-size: $font-size * 2;}[data-dpr="3"] & {font-size: $font-size * 3;} }有了這樣的混合宏之后,在開發中可以直接這樣使用:
@include font-dpr(16px);當然這只是針對于描述性的文本,比如說段落文本。但有的時候文本的字號也需要分場景的,比如在項目中有一個slogan,業務方希望這個slogan能根據不同的終端適配。針對這樣的場景,完全可以使用?rem?給slogan做計量單位。
CSS
本來想把這個頁面的用到的CSS(或SCSS)貼出來,但考慮篇幅過長,而且這么簡單的頁面,我想大家也能輕而易舉搞定。所以就省略了。權當是給大家留的一個作業吧,感興趣的可以試試Flexible能否幫你快速完成H5頁面終端適配。
效果
最后來看看真機上顯示的效果吧。我截了兩種設備下的效果:
iPhone4
iPhone6+
總結
其實H5適配的方案有很多種,網上有關于這方面的教程也非常的多。不管哪種方法,都有其自己的優勢和劣勢。而本文主要介紹的是如何使用Flexible這樣的一庫來完成H5頁面的終端適配。為什么推薦使用Flexible庫來做H5頁面的終端設備適配呢?主要因為這個庫在手淘已經使用了近一年,而且已達到了較為穩定的狀態。除此之外,你不需要考慮如何對元素進行折算,可以根據對應的視覺稿,直接切入。
當然,如果您有更好的H5頁面終端適配方案,歡迎在下面的評論中與我們一起分享。如果您在使用這個庫時,碰到任何問題,都可以在Github給我們提?Issue?。我們團隊會努力解決相關需Issues。
總結
以上是生活随笔為你收集整理的使用Flexible实现手淘H5页面的终端适配的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: rails 代码结构详解
- 下一篇: [UOJ]#36. 【清华集训2014】