1. 程式人生 > >使用Flexible實現手淘H5頁面的終端適配

使用Flexible實現手淘H5頁面的終端適配

此段程式碼實現動態計算,事實上他做了這幾樣事情:

  • 動態改寫 <meta> 標籤
  • 給 <html> 元素新增 data-dpr 屬性,並且動態改寫 data-dpr 的值
  • 給 <html> 元素新增 font-size 屬性,並且動態改寫 font-size 的值

程式碼:

;(function(win, lib) {
    var doc = win.document;
    var docEl = doc.documentElement;
    var metaEl = doc.querySelector('meta[name="viewport"]');
    var flexibleEl = doc.querySelector('meta[name="flexible"]');
    var dpr = 0;
    var scale = 0;
    var tid;
    var flexible = lib.flexible || (lib.flexible = {});
    
    if (metaEl) {
        console.warn('將根據已有的meta標籤來設定縮放比例');
        var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
        if (match) {
            scale = parseFloat(match[1]);
            dpr = parseInt(1 / scale);
        }
    } else if (flexibleEl) {
        var content = flexibleEl.getAttribute('content');
        if (content) {
            var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
            var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
            if (initialDpr) {
                dpr = parseFloat(initialDpr[1]);
                scale = parseFloat((1 / dpr).toFixed(2));    
            }
            if (maximumDpr) {
                dpr = parseFloat(maximumDpr[1]);
                scale = parseFloat((1 / dpr).toFixed(2));    
            }
        }
    }

    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;
    }

    docEl.setAttribute('data-dpr', dpr);
    if (!metaEl) {
        metaEl = doc.createElement('meta');
        metaEl.setAttribute('name', 'viewport');
        metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
        if (docEl.firstElementChild) {
            docEl.firstElementChild.appendChild(metaEl);
        } else {
            var wrap = doc.createElement('div');
            wrap.appendChild(metaEl);
            doc.write(wrap.innerHTML);
        }
    }

    function refreshRem(){
        var width = docEl.getBoundingClientRect().width;
        // if (width / dpr > 540) {
        //     width = 540 * dpr;
        // }
        var rem = width / 10;
        docEl.style.fontSize = rem + 'px';
        flexible.rem = win.rem = rem;
    }

    win.addEventListener('resize', function() {
        clearTimeout(tid);
        tid = setTimeout(refreshRem, 300);
    }, false);
    win.addEventListener('pageshow', function(e) {
        if (e.persisted) {
            clearTimeout(tid);
            tid = setTimeout(refreshRem, 300);
        }
    }, false);
    win.addEventListener("orientationchange", function() {
        clearTimeout(timer);
        timer = setTimeout(setFontSize, 300);
    }, false);

    if (doc.readyState === 'complete') {
        doc.body.style.fontSize = 12 * dpr + 'px';
    } else {
        doc.addEventListener('DOMContentLoaded', function(e) {
            doc.body.style.fontSize = 12 * dpr + 'px';
        }, false);
    }
    

    refreshRem();

    flexible.dpr = win.dpr = dpr;
    flexible.refreshRem = refreshRem;
    flexible.rem2px = function(d) {
        var val = parseFloat(d) * this.rem;
        if (typeof d === 'string' && d.match(/rem$/)) {
            val += 'px';
        }
        return val;
    }
    flexible.px2rem = function(d) {
        var val = parseFloat(d) / this.rem;
        if (typeof d === 'string' && d.match(/px$/)) {
            val += 'rem';
        }
        return val;
    }

})(window, window['lib'] || (window['lib'] = {}));


曾幾何時為了相容IE低版本瀏覽器而頭痛,以為到Mobile時代可以跟這些麻煩說拜拜。可沒想到到了移動時代,為了處理各終端的適配而亂了手腳。對於混跡各社群的偶,時常發現大家拿手機淘寶的H5頁面做討論——手淘的H5頁面是如何實現多終端的適配

那麼趁此Amfe阿里無線前端團隊雙11技術連載之際,用一個實戰案例來告訴大家,手淘的H5頁面是如何實現多終端適配的,希望這篇文章對大家在Mobile的世界中能過得更輕鬆。

目標

拿一個雙11的Mobile頁面來做案例,比如你實現一個類似下圖的一個H5頁面:

Flexible實現手淘H5頁面的終端適配

目標很清晰,就是做一個這樣的H5頁面。

DEMO

請用手機掃下面的二維碼

DEMO

痛點

雖然H5的頁面與PC的Web頁面相比簡單了不少,但讓我們頭痛的事情是要想盡辦法讓頁面能適配眾多不同的終端裝置。看看下圖你就會知道,這是多麼痛苦的一件事情:

Flexible實現手淘H5頁面的終端適配

點選這裡檢視更多終端裝置的引數。

再來看看手淘H5要適配的終端裝置資料:

Flexible實現手淘H5頁面的終端適配

看到這些資料,是否死的心都有了,或者說為此捏了一把汗出來。

手淘團隊適配協作模式

早期移動端開發,對於終端裝置適配問題只屬於Android系列,只不過很多設計師常常忽略Android適配問題,只出一套iOS平臺設計稿。但隨著iPhone6,iPhone6+的出現,從此終端適配問題不再是Android系列了,也從這個時候讓移動端適配全面進入到“雜屏”時代。

Flexible實現手淘H5頁面的終端適配

為了應對這多麼的終端裝置,設計師和前端開發之間又應該採用什麼協作模式?或許大家對此也非常感興趣。

而整個手淘設計師和前端開發的適配協作基本思路是:

  • 選擇一種尺寸作為設計和開發基準
  • 定義一套適配規則,自動適配剩下的兩種尺寸(其實不僅這兩種,你懂的)
  • 特殊適配效果給出設計效果

還是上一張圖吧,因為一圖勝過千言萬語:

Flexible實現手淘H5頁面的終端適配

在此也不做更多的闡述。在手淘的設計師和前端開發協作過程中:手淘設計師常選擇iPhone6作為基準設計尺寸,交付給前端的設計尺寸是按750px * 1334px為準(高度會隨著內容多少而改變)。前端開發人員通過一套適配規則自動適配到其他的尺寸。

根據上面所說的,設計師給我們的設計圖是一個750px * 1600px的頁面:

Flexible實現手淘H5頁面的終端適配

前端開發完成終端適配方案

拿到設計師給的設計圖之後,剩下的事情是前端開發人員的事了。而手淘經過多年的摸索和實戰,總結了一套移動端適配的方案——。

這種方案具體在實際開發中如何使用,暫時先賣個關子,在繼續詳細的開發實施之前,我們要先了解一些基本概念。

一些基本概念

在進行具體實戰之前,首先得了解下面這些基本概念(術語):

視窗 viewport

簡單的理解,viewport是嚴格等於瀏覽器的視窗。在桌面瀏覽器中,viewport就是瀏覽器視窗的寬度高度。但在移動端裝置上就有點複雜。

移動端的viewport太窄,為了能更好為CSS佈局服務,所以提供了兩個viewport:虛擬的viewportvisualviewport和佈局的viewportlayoutviewport。

George Cummins在Stack Overflow上對這兩個基本概念做了詳細的解釋

而事實上viewport是一個很複雜的知識點,上面的簡單描述可能無法幫助你更好的理解viewport,而你又想對此做更深的瞭解,可以閱讀PPK寫的相關教程

物理畫素(physical pixel)

物理畫素又被稱為裝置畫素,他是顯示裝置中一個最微小的物理部件。每個畫素可以根據作業系統設定自己的顏色和亮度。正是這些裝置畫素的微小距離欺騙了我們肉眼看到的影象效果。

Flexible實現手淘H5頁面的終端適配

裝置獨立畫素(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可以用來輔助區分視網膜裝置還是非視網膜裝置。

縮合上述的幾個概念,用一張圖來解釋:

Flexible實現手淘H5頁面的終端適配

眾所周知,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翻譯的一張資訊圖:

Flexible實現手淘H5頁面的終端適配

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系列,始終認為其dpr1

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模板

<!DOCTYPE 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">
        <
            
           

相關推薦

使用Flexible實現H5頁面終端rem自適應佈局-移動端自適應必備

曾幾何時為了相容IE低版本瀏覽器而頭痛,以為到Mobile時代可以跟這些麻煩說拜拜。可沒想到到了移動時代,為了處理各終端的適配而亂了手腳。對於混跡各社群的偶,時常發現大家拿手機淘寶的H5頁面做討論——手淘的H5頁面是如何實現多終端的適配? 那麼趁此Amfe阿里無線前端團

使用Flexible實現H5頁面終端

此段程式碼實現動態計算,事實上他做了這幾樣事情: 動態改寫 <meta> 標籤給 <html> 元素新增 data-dpr 屬性,並且動態改寫 data-dpr 的值給 <html> 元素新增 font-size 屬性,並且動態改寫 

使用Flexible實現H5頁面終端【轉】

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta content="yes" name="apple-mobile-web-

Flexible實現H5頁面的rem佈局

前言 專案加上了縮放比例, 但是ionic本身很多元件都是px的 導致在github上的外掛總是這樣那樣的問題弄得心很累, 一氣之下。直接全部替換成了淘寶的H5方案,反正改的也不少 匯入 方案1. 直接阿里雲CDN 在單頁index.html中加入

css 手機 H5移動端方案flexible原始碼分析

手淘H5移動端適配方案flexible原始碼分析   移動端適配一直是一個值得探討的問題,在業餘時間我找了一些頁面,查看了一些廠商對於移動端H5頁面的適配方案,看到了幾個典型的例子,今天就來記錄一下我看到的第一個典型的例子,也是我們公司目前普通H5專案正在使用的適配方案。

H5移動端方案flexible原始碼分析

移動端適配一直是一個值得探討的問題,在業餘時間我找了一些頁面,查看了一些廠商對於移動端H5頁面的適配方案,看到了幾個典型的例子,今天就來記錄一下我看到的第一個典型的例子,也是我們公司目前普通H5專案正在使用的適配方案。 這個適配方案是lib-flexible,在看這個原始碼的同時,我想先來回顧一下幾個概念:

教你如何用 lib-flexible 實現移動端H5頁面

前話 好久沒寫教程了(可能會誤導新手的菜鳥教程( ̄▽ ̄)”)。 最近入職公司做前端實習,這幾個星期來學到了移動端H5頁面適配。(以前根本沒做過移動端網頁/(ㄒoㄒ)/~~,還是微信端的) 所以把我學到的一個小知識點寫下來,也分享給前端新手們。 正

rem、px、em(手機端h5頁面螢幕的幾種方法)

px px畫素(pixel):相對長度單位。相對於顯示器螢幕解析度而言。pc端使用px倒也無所謂,可是在移動端,因為手機解析度種類頗多,不可能一個個去適配,這時px就顯得非常無力,所以就要考慮em和r

Flexible實現H5頁面終端

設計師和前端開發的適配協作基本思路是: 選擇一種尺寸作為設計和開發基準 定義一套適配規則,自動適配剩下的兩種尺寸(其實不僅這兩種,你懂的) 特殊適配效果給出設計效果 lib-flexible是什

iPhone X Q H5 頁面通用解決方案

本文作者:林煥彬 導語: iPhone X的出現,一方面對於整個手機行業的發展極具創新領頭羊的作用,另一方面也對現有業務的頁面適配帶來了新的挑戰。 對於手Q中的各業務來說,受iPhone X影響的H5頁面挺多,應該採取什麼快速有效的辦法來應對呢? 目前的H5

h5 web頁面手機

;(function () { window.addEventListener(('orientationchange' in window ? 'orientationchange' : 'resize'), (function () { funct

基於應用寶實現微信h5頁面中開啟本地app,如果沒有跳轉下載頁面的解決方案

首先這個方法是基於微信中開啟的h5頁面的,如果是外接瀏覽器的話則無論是否有該app都會執行下載 <a href="http://d.xiaojukeji.com/c/73852">

移動端頁面

ice minimum meta view ini maximum dev port pan   不進行任何配置,把網頁直接放在移動端打開會有嚴重的縮小頁面問題,通常我們可以在head標簽中加入以下代碼就可以解決:  <meta name="viewport" con

再聊移動端頁面

nim aspect width 超過 ans 一起 device 效果 其他 Flexible承載的使命 Lexible到今天也有幾年的歷史了,解救了很多同學針對於H5頁面布局的適配問題。而這套方案也相對而言是一個較為成熟的方案。簡單的回憶一下,當初為了能讓頁面更好的適配

移動端頁面開發 rem佈局原理

什麼是適配,為什麼要適配 我們拿到的設計圖一般是以640,750,1080解析度為基準設計的,而現在的手機終端各式各樣,解析度不同,邏輯畫素不同 ,視口不同,所以為了讓我們的頁面在每個裝置上都可以良好的展示,那麼就需要為這些裝置做統一的處理,這個過程就稱為移動端適配。

H5 font-size

https://blog.csdn.net/highboys/article/details/78762744方法一:http://www.mamicode.com/info-detail-1816919.html例如以螢幕320畫素為基準html {font-size: 6

h5 手機螢幕—REM

一、rem、em和px之間的關係     使用rem之前,先得弄清楚rem、em和px之間的關係,特別是每一個單位的使用跟程式碼塊的繼承之間的關係:          通過對比會發現:只是單位使用不一樣但效果卻是截然不同的。rem和em都是相對單位,

CSS3前端頁面使用rem換算

轉自:https://www.cnblogs.com/annie211/p/8118857.html為什麼要使用rem之前有些適配做法,是通過js動態計算viewport的縮放值(initial-scale)。例如以螢幕320畫素為基準,設定1,那螢幕375畫素就是375/3

前端頁面使用rem換算---rem詳解

為什麼要使用rem 僅用於學習,違者必究!!! 之前有些適配做法,是通過js動態計算viewport的縮放值(initial-scale)。 例如以螢幕320畫素為基準,設定1,那螢幕375畫素就是375/320=1.18以此類推。 但直接這樣強制頁面縮放過於

頁面字型(media query ), em的使用

首先來說說em的使用 使用者的瀏覽器預設渲染的文字大小是“16px”,換句話說,Web頁面中“body”的文字大小在使用者瀏覽器下預設渲染是“16px”。 而“em”是一個相對的大小,是其相對於元素父元素的大小。比如說:如果在一個< div >設