【收集-轉載】前端佈局

感謝csdn原文:淺談前端移動端頁面開發(佈局篇)
感謝:移動前端自適應解決方案和比較
使用Flexible實現手淘H5頁面的終端適配html

一. viewport

  1. 什麼是viewport
    簡單來說,viewport就是瀏覽器上,用來顯示網頁的那一部分區域了,也就是說,瀏覽器的實際寬度,是和咱們手機的寬度不同的,不管你的手機寬度是320px,仍是640px,在手機瀏覽器內部的寬度,始終會是瀏覽器自己的viewport。現在的瀏覽器,都會給本身的自己提供一個viewport的默認值,多是980px,或者是其餘值。就以手機來講吧,目前,新版本的手機瀏覽器,絕大部分是以980px做爲默認的viewport值的。我這裏對新版本的不一樣平臺下的瀏覽器作了測試,通過測試,iphone下的默認viewport爲980px,安卓下的瀏覽器,目前主流的最新瀏覽器(好比chrome,還有不少國產的像qq,uc)的viewport也是980px了。前端

  2. viewport是用來幹什麼的
    viewport的默認值,通常來講是大於手機屏幕的。這樣就能夠作到當咱們在瀏覽桌面端網頁的時候,可讓桌面端端網頁正常顯示(咱們普通頁面設計的時候,通常頁面的主區域是以960px來作的,因此980px這個值,能夠作到桌面端網頁的正常顯示)。可是,其實咱們手機的屏幕寬度是沒有960px的,所以瀏覽器會出現橫向滾動條。同時,即便是基於980的viewport,咱們在移動端瀏覽咱們的桌面頁面的體驗其實也並很差,因此,通常的,咱們會專門給瀏覽器設計一個移動端的頁面。android

  3. 對viewport的控制
    現在能夠絕大部分瀏覽器裏(即主流的安卓瀏覽器和iOS),都支持對viewport的一個控制了。通常的,咱們會這麼寫。ios

viewport默認有6個屬性
width: 設置viewport的寬度(即以前所說起到的,瀏覽器的寬度詳),這裏能夠爲一個整數,又或者是字符串"width-device"git

initial-scale: 頁面初始的縮放值,爲數字,能夠是小數
minimum-scale: 容許用戶的最小縮放值,爲數字,能夠是小數
maximum-scale: 容許用戶的最大縮放值,爲數字,能夠是小數
height: 設置viewport的高度(咱們通常而言並不能用到)
user-scalable: 是否容許用戶進行縮放,'no'爲不容許,'yes'爲容許

咱們把這個標籤是在head裏面,像這樣github

<meta name="viewport" content="initial-scale=1">

這樣就能夠作到對viewport的控制了chrome

二. 關於咱們的設備

三個須要瞭解的概念:瀏覽器

  1. PPI: 能夠理解爲屏幕的顯示密度app

  2. DPR: 設備物理像素和邏輯像素的對應關係,即物理像素/邏輯像素iphone

  3. Resolution: 就是咱們常說的分辨率

物理像素與邏輯像素
看了咱們上面內容一的第一點以後,或許有些人會有些疑問,個人安卓手機,或者iphone6plus(目前應該僅限於這一款機型吧),買回來的是1920x1080的或者其餘更高的,比我以前所謂的那個viewport默認的980px要大。
這樣的問題,也就是我以前所說的物理像素與邏輯像素的關係了(即DPR)。以1920x1080爲例,1080爲物理像素,而咱們在viewport中,獲取到的,好比"width-device",是邏輯像素。因此以前viewport的默認值,所比對的大小,實際上是邏輯像素的大小,而非物理像素的大小。
以iphone6爲例,在不作任何縮放的條件下,iphone6的獲取到的'width-device'爲375px,爲屏幕的邏輯像素。而購買時咱們所知的750px,則爲屏幕的物理像素。

CSS的問題
有了上面第二點的一些基礎,仍是以iphone6爲例,咱們能夠知道,其實咱們所寫的1px,在iphone6上爲2px的物理像素。因此,最後的,給出一個結論。就是咱們寫的1px,在移動端,是邏輯像素的1px,而非物理像素的1px。

三. 使用rem佈局

簡單說下rem
rem是根據頁面的根元素的font-size的一個相對的單位,即

html{
    font-size: 16px;
}

好比當咱們在一個div中,如此寫

div{
    width: 2rem;
}

那麼咱們的width,是16*2 = 32px
rem作到適配不一樣分辨率
這個是如今手機淘寶的移動端的解決方案,即便用rem的特性,來對頁面進行佈局。
下面舉一個例子
假定設計稿的大小爲750,那麼咱們則將整個圖分紅100份來看(下面的題外話會說明爲何會分紅100份來看)
那麼,咱們如今就讓根部元素的font-size爲75px

html{
    font-size: 75px;
}

那麼,咱們如今就能夠比對設計稿,好比設計稿中,有一個div元素,寬度,高度都爲75px,那麼咱們這樣寫便可

div{
    height: 1rem;
    width: 1rem;
}

可能看到這裏,一些人仍是不明白怎麼用rem作到適配不一樣的分辨率,那麼咱們再來
如今,咱們換設備了,不用這個設備是一個width爲640的手機
那麼這個時候,咱們的rem單位就起到做用了。
咱們的rem全是根據html的font-size來改變的,因此說,這個時候,咱們只須要把html下的font-size改爲64px。那麼,咱們以前的div,由於是根據html下的font-size動態變化的,那麼。此時也就變成了寬度和高度都爲64px的東西了。這樣,就能夠作到適配不一樣的屏幕分辨率了。(其實就是個等比縮放)
總結一下,咱們的解決方案,其實就是 設計稿的像素/html的font-size = 用來代替px的rem。
這一個步驟,咱們須要經過JS來進行操做。
對於js的操做在下面會提到。

  • DPR的問題

視覺姐姐給了咱們設計稿,並交由咱們實現,那麼,咱們應該去認真的實現:-)(試想你作了一張圖,而前端不少地方並無按照你所想的,你所給的去作,而是私自改變了不少東西,你確定會不高興的)
那麼1px會出現什麼問題呢。
還記得咱們第二大點講的,咱們的設備,是有物理像素和邏輯像素的。而假設咱們的設計稿是750的,同時仍是以iphone6爲例,此時若是咱們的viewport是這樣的

<meta name="viewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

以前說過,在不作任何縮放的條件下,iphone6獲取到的viewport爲375px。
而後咱們的頁面中有個div,他有一個邊框值,以下

div{
    height: 5rem;
    widht:5rem;
    border: 1px solid #000
}

此時咱們寫的1px,其實是邏輯像素,而咱們在iphone6上看到的是物理像素,因而這個時候,咱們眼睛所看到的實際上是2px(參考第二點第三個問題)
因此此時咱們須要在viewport上作文章了,此時先明確,若是要獲取到真正的1px,那麼咱們須要這麼作,將viewport改成

<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">

即對屏幕作0.5倍的縮放。這樣,咱們就能獲得實際的1px。
因此到這裏,咱們還要明確一點,viewport的meta標籤,咱們這裏也只能經過js來動態生成。
同時,這樣寫,聽說還能夠避免好比inline的SVG等元素按照邏輯像素的渲染。避免了整個頁面清晰度的打折(其實我並不能看出來)
文字適配問題
最近深深糾結與rem與px作字體單位的問題,仍是先分別談下兩者吧。

  1. rem與px的特色:
    以rem做爲字體單位:咱們可讓頁面總體的文字,也跟隨着html的font-size來進行改變,這樣,在不一樣的屏幕下,能夠作到文字相對屏幕的比例是同樣的。

以px做爲字體單位: 這個是目前不少網站仍是依然採用的方法。由於以上面所寫的,以rem做爲字體單位。不管在任何屏幕下面,咱們的文字都會根據屏幕作一個適應。試想這樣一個場景。你買了一個大屏手機(5.7寸的),而別人用的是4寸的手機。以rem做爲字體單位的話,那大屏手機看到的文字多少和小屏手機確實同樣的了。這樣來作,其實並不符合咱們買大屏手機的期待。同時,以rem做爲字體單位,可能會致使出現不少奇怪的字體大小(畢竟是根據html的font-size動態變化的嘛),同時這其中還涉及到了一個點陣尺寸的概念,這個在下面來說。

  1. 字體大小引起的系列問題:
    字體大小:咱們平時也看過,不少網站,是不以奇數做爲字體大小的。我稍微查了些東西,在知乎上的如今網頁設計中的爲何少有人用 11px、13px、15px 等奇數的字體?問題下,有一些比較好的解答,我就再也不多說(我也並不能比這個問題說的更多),總的來講,其實就是偶數寬度的字體可以顯得均衡,以及一個點陣的問題。不過由於要談及點陣,因此我拿上面回答中的一個內容舉例。

假若一個字體,只提供了12px,14px,16px的點陣。那麼當你寫13px,15px,17px的時候。就並無其字體大小所對應的點陣。那麼這樣就形成了一個問題。他們會使用其相鄰的點陣,好比對應使用了12px,14px,16px的點陣,而致使一個問題,文字佔用的大小確實改變,但點陣卻並無改變。

  1. 文字適配的解決方案:
    上面說了這麼多,咱們總要有一套解決方案吧

對於一些標題性的文字,咱們依然能夠用rem。讓他隨着屏幕來進行縮放,由於標題性文字通常較大,而較大的文字,點陣對其影響就越小。這樣,即便出現奇怪的尺寸,也可以讓字體獲得很好的渲染。
對於一些正文內容的文字(即站在使用者的角度,你不但願他進行縮放的文字)。咱們採用px來進行處理。

四.安卓與ios不得不說的問題(解決篇)

在 三.使用rem佈局 裏面,咱們給出了各類狀況的解決方案,而且,在我舉例的時候,熱衷於使用iphone來舉例,但其實,上面的全部問題,不是僅僅iphone會出現的問題,安卓也是同樣。可是,若是你已經看完了上面,那麼這裏,纔是真正給出咱們解決方案的地方,而且,這個解決方案並不完善。
談談iphone的r屏與安卓的各類屏
rem佈局也好,用viewport進行縮放也罷,文字的適配問題也是,都是基於咱們想對各個不一樣的設備所進行的匹配。這套方案很好,然而也有其兼顧不到的地方。即安卓和ios的屏幕的一些問題,固然,細的東西咱們不談,咱們只談dpr。
先談iphone
其實iphone爲開發者考慮到了不少東西,爲了讓開發者便於開發,在6plus出現以前,iphone的dpr始終也就是2(即前面所談的物理像素/邏輯像素=2),即便是6plus出現了,iphone到底其實也就只有2,3這兩個dpr。咱們很容易對其作到兼顧。
再談安卓
安卓並無對本身的屏幕叫作r屏,可是其原理和iphone的r屏能夠說是同樣。r屏作的是什麼,把兩個(三個)物理像素,丟到了一個邏輯像素裏面,讓屏幕展示的更清晰(固然,這是我片面的理解,不過我以爲大致來講並無錯,咱們也不用去深刻探討r屏還有什麼東西,我也並不懂)。而安卓也是同樣,他也一樣把n個物理像素丟到了一個邏輯像素裏面。而這裏的n,也就是dpr值(因此當我看到好多人問安卓爲何不採用r屏的時候,我真的也是……醉了?)。而安卓的dpr值,並不像iphone那樣,就只有兩個值。安卓的dpr是千奇百怪的,多是1.5,2,3,4,2.5等等的都有。(甚至我還看到了1.7之類的,安卓的各個設備商,玩的真尼瑪high啊。怎麼高興怎麼來。)
因此,對安卓的屏幕的dpr的處理,實際上是很頭疼的,由於,他和咱們對字體的處理,有了很大的衝突。這個在下面說起
首先看看手淘的解決方案
rem佈局
用js獲取到頁面的寬度,而後對其進行寬度/10的處理,再將其寫到html的font-size中。手淘的flexible.js裏面的這一部分,併爲了方便看懂作了些改寫。大致就是這樣的
function refreshRem(){

var docEl = window.document.documentElement;
var width = docEl.documentElement.getBoundingClientRect().width;

var rootSize = width/10;
docEl.style.fontSize = rootSize + 'px';

}
dpr的配置
首先,在引入flexible.js以前,咱們能夠對dpr進行手動的配置,即便用自定義的meta標籤來配置dpr(看清楚是flexible,而非viewport)
<meta name="flexible" content="initial-dpr=2,maximum-dpr=3" />
iniital-dpr是把dpr強制設定爲給定的值,而maximum-dpr則是給出一個最大的dpr限制,而後對其和系統的dpr作一個比較。
而後依然爲了方便閱讀我把flexble.js這一部分的代碼抽象出來,

var doc = window.document
    var metaEl = doc.querySelector('meta[name="viewport"]');
    var flexibleEl = doc.querySelector('meta[name="flexible"]');
    var dpr = 0;
    var scale = 0;            //縮放比例
    //在meta標籤中,已經有了viewport,則使用已有的viewport,並根據meta標籤,對dpr進行設置
    if (metaEl) {
        console.warn('將根據已有的meta標籤來設置縮放比例');
        var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
        if (match) {
            scale = parseFloat(match[1]);
            dpr = parseInt(1 / scale);
        }
    //若是在meta標籤中,咱們手動配置了flexible,則使用裏面的內容
    } 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));
}
}
}

這樣,咱們經過flexible的分析與獲取,對dpr進行了書寫。不過其實這裏,是有個問題的。即在書寫maximum的的狀況下,其實根本沒有像文檔中給咱們的說法同樣,作一個比較,而是作了和initialDpr同樣的一個處理。不過這裏也不對其作一個探討了。
而後,這套解決方案,而後當咱們在meta標籤裏面並無對viewport以及flexible兩個的任意一個進行書寫的時候,他也是會自動獲取一個dpr值的

if (!dpr && !scale) {
    var isAndroid = window.navigator.appVersion.match(/android/gi);
    var isIPhone = window.navigator.appVersion.match(/iphone/gi);、
    //devicePixelRatio這個屬性是能夠獲取到設備的dpr的
    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;
}
這裏咱們能夠看到,手機淘寶並無對安卓的dpr進行一個適配,緣由以後再講。
而後到了這裏,咱們獲取到了咱們須要的dpr值,並根據dpr值獲取到了咱們所須要的縮放值(即scale)
而後咱們要作的,就是在並無viewport的meta標籤對狀況下本身動態將這個標籤寫進咱們的header,形式是這樣的
<meta name="viewport" content="initial-scale=scale, maximum-scale=scale, minimum-scale=scale, user-scalable=no">
這樣,dpr的配置,也就完成了,固然,安卓設備並無對dpr進行一個配置(上面的動態生成就不給出js了)
文字的解決方案
因爲手淘暫時並無對安卓作一個處理,因此,這裏,只是對iphone作了一個處理
即在html上,加入了一個自定義屬性,data-dpr。
<html data-dpr='dpr'></html>
仍是以750的設計稿爲例(即iphone6)
假如設計稿上某a標籤是32px,那麼,咱們要這麼寫
a{
    font-size: 16px
}
/*iphone6*/
[data-dpr='2'] a{
    font-size: 32px
}
/*iphone6plus*/
[data-dpr='3'] a{
    font-size: 32px
}

如今的一些問題
正如咱們看到的,手淘目前的方案裏面,是沒有考慮到安卓dpr的問題的。即,這套方案,只對於iphone的r屏作了一個處理,而對於安卓,並無作dpr的處理。咱們來分析下緣由吧(我的拙見)。
咱們但願字體可以以px來展示,同時,咱們也但願咱們的東西能對dpr作一個適配。對於ios,這天然是可行的,即採用了data-dpr的自定義屬性來調整文字。4到6寫一套字體大小,6p寫一套字體大小,而後在對dpr爲1的屏幕寫一套字體大小。其實這種寫法仍是很噁心,不過基於對dpr的適配,這樣寫也算是個解決方案了。
不過一樣的解決方案到安卓就不行了,安卓的dpr有時候會很亂(好比如今在goole的手機測試裏面能夠看到,安卓的dpr,lg的某些設備還採用了1.7那樣的奇怪dpr)。而當1.7dpr這種不規範的數字出現的時候,咱們就不能採用以前的解決方案了,好比
[data-dpr='1.7'] a{

font-size: 25px

}
這樣的東西是不可能去寫的,那萬一還有2.25,2.5之類的呢?咱們都要拿去匹配麼?
其實如今,由於咱們經過devicePixelRatio能夠獲取到安卓的dpr值,便可以作到對安卓設備的dpr一個匹配。可是,文字若是採用px的話,確實是很難作到匹配的。
即總結一下,就是說,對於安卓的dpr匹配,目前來講,是沒有什麼問題的,可是,對於dpr匹配以後的字體,那確定是有問題的。
常見的dpr下的字體,咱們依然能夠解決,可是不常見的dpr,咱們確實很難作到對dpr的解決。那如何解決這些問題呢。目前以我本人這個不太靈光的腦殼,確實也不曉得該如何進行一個處理了,起碼作不到很好的解決。
不過,仍是丟上些我的的觀點吧。
在以前的對dpr的判斷中,是根據了設備進行判斷,即安卓不對dpr進行改變,僅對ios的設備進行改變。那麼,咱們其實可不能夠以dpr的值來作一個處理呢?即像這樣寫

if (!dpr && !scale) {
    //devicePixelRatio這個屬性是能夠獲取到設備的dpr的
    var devicePixelRatio = win.devicePixelRatio;
    //判斷dpr是否爲整數
    var isRegularDpr = devicePixelRatio.toString().match(/^[1-9]\d*$/g)
    if (isRegularDpr) {
    // 對因而整數的dpr,對dpr進行操做
     if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {                
        dpr = 3;
    } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
        dpr = 2;
    } else {
        dpr = 1;
    }
} else {
    // 對於其餘的dpr,人採用dpr爲1的方案
    dpr = 1;
    }
    scale = 1 / dpr;
}

咱們對這裏作了一點點修改,即來判斷dpr是不是規則的,也就是是不是咱們常見的1,2,3等,而後,咱們只對規則的dpr,來進行一個字體的處理。這樣,iphone依然仍是用以前的匹配方案。而其實目前安卓,不少的設備仍是比較常見的dpr了,因此咱們這裏,將以前對設備的判斷,轉變成對dpr是不是整數的一個判斷。其餘地方不變,能夠解決對安卓dpr的部分匹配。
一樣,開發的時候,若是並不在意字體的問題的話,大能夠直接使用rem。那樣是能夠作到dpr和文字都適配的問題。不過正如咱們講到字體的時候所說的,使用rem是不少用戶不但願的(大屏機仍是和小屏機看到同樣多的內容),同時,還有點陣的問題。

參考

手機淘寶的flexible設計與實現

題外話:

  • iphone6plus頗有趣的地方

iphone6plus照理來講的,其實際dpr是2.87左右的,不過,爲了方便開發者來開發,iphone6plus對其作了一個調整,將dpr調整爲3,而後在對屏幕進行了一個縮放。這樣作,天然是方便了開發者前去開發,然而,這樣作,也有了一些性能上的損失。(iphone爲開發者考慮的仍是挺周全的,看看隔壁安卓,dpr怎麼爽怎麼來,都特麼本身玩本身的)

  • 有意思的vh和vw

vh,vw目前還存在很大程度的兼容性問題,因此還並無採用。
vh,vw有什麼特色呢
這兩個元素分別會把屏幕上的可視高度(說通俗點就是你手機屏幕那個框框頭裝起的東西),寬度,分紅100份來看,好比先前咱們用rem來處理的地方,咱們須要在html元素下寫上font-size: 75px,而後再在div下寫上width:1rem。而有了vh,vw以後,咱們如此處理html的font-size就好。
html {

font-size: 10vw;

}這樣寫,省去了一部js操做的步驟。

相關文章
相關標籤/搜索