再談移動端適配和點5像素的由來

前言

這篇文章的內容如題目同樣,主要分爲兩個部分,javascript

  1. 談談業內主流的移動端適配解決方案css

  2. 點5像素的由來和實現方法html

建議在讀這篇文章的時候先讀下這篇文章《高清屏概念解析與檢測設備像素比的方法_20161005》,由於這些文章涉及的不少概念在這篇文章中都會提到。前端

1.再談移動端適配

1.1百分比解決方案的缺點

在咱們的項目中大量的使用百分比來解決頁面在寬度上的自適應,其實在高度上並無很好的自適應。因此在咱們的前端頁面會出現一些比較奇怪的問題。好比下面這樣:java

還有一個比較明顯的問題就是:在任何機型上咱們的按鈕的高度是不會變化自適應的,因此小屏手機咱們的按鈕看起來很臃腫。css3

1.2一些經常使用單位的概念解析

在談我爲何在咱們的項目中引入rem單位的時候,先來詳細的瞭解幾個經常使用的單位概念。git

  1. px像素(Pixel)。相對長度單位。像素px是相對於顯示器屏幕分辨率而言的(也就是說是跟物理設備有關的)。拿高清屏和普通屏來作對比就是普通屏幕的1個像素點就是1個物理像素點,而高清屏的1個像素點4個物理像素點github

  2. em相對長度單位。相對於當前對象內文本的字體尺寸。如當前對行內文本的字體尺寸未被人爲設置,則相對於瀏覽器的默認字體尺寸。em單位的特色:1. em的值並非固定的;2. em會繼承父級元素的字體大小。web

  3. rem相對長度單位。rem是CSS3新增的一個相對單位,這個單位引發了普遍關注。這個單位與em有什麼區別呢?區別在於使用rem爲元素設定字體大小時,仍然是相對大小,但相對的只是HTML根元素。這個單位可謂集相對大小和絕對大小的優勢於一身,經過它既能夠作到只修改根元素就成比例地調整全部字體大小,又能夠避免字體大小逐層複合的連鎖反應。chrome

任意瀏覽器的默認字體高都是16px。全部未經調整的瀏覽器都符合: 1rem=16px。那麼12px=0.75rem,10px=0.625rem。爲了簡化font-size的換算,須要在css中的html選擇器中聲明font-size=62.5%,這就使rem值變爲 16px*62.5%=10px, 這樣12px=1.2rem, 10px=1rem, 也就是說只須要將原來的px數值除以10,而後換上rem做爲單位就好了。

注意:當咱們在根節點<html>上設置了font-size基準值之後,在文檔中有使用rem單位的屬性值都是相對於根節點font-size的一個相對值。好比說一些元素的屬性如width height margin等。也正是這個緣由,如今不少網站的移動端網站都在使用rem單位做爲適配工具。

在使用rem的時候比較麻煩的就是pxrem換算的問題。上面的除10的方案是比較簡單的。可是根據設置基準值的不一樣換算方法也不同。若是咱們使用scss來寫咱們的樣式表的話,解決方法就比較簡單了,代碼以下:

@function px2rem($px){
    @return ($px/10)/2 + rem;  //  至關於$px/20  +rem
}

width:px2rem(100px);   //5rem
height:px2rem(200px);  //10rem

1.3爲何引入 rem 單位

由上面的內容已經知道百分比單位在多屏幕適配上的缺點和rem單位的優勢。那麼rem單位能爲咱們的開發帶來什麼呢?來看一個常見的多列布局,淘寶移動端的商品列表頁,以下圖:

設計稿(不論是iphone6的二倍稿仍是iphone5的二倍稿)中給展現商品的坑位的寬高比是固定的。爲了可以使這個列表在不一樣的屏幕上達到最佳的顯示效果,須要保持寬度和高度比一致。若是使用百分比確定是知足不了這個需求的,由於高度上咱們沒有辦法控制。

iphone6和iphone5的屏幕寬度和高度比不同,當咱們使用百分比作多列布局的時候就會出現下面的這種狀況:

因此爲了知足商品坑位在不一樣屏幕上的寬高比一致,淘寶使用的是rem的解決方案,也是目前最好的解決方案。

1.3爲何將計算根元素的font-size值的js放在head標籤中

設置根節點font-size值的方法,第一種是使用css的Media queries,示例代碼以下:

@media (min-device-width : 375px) and (max-device-width : 667px) and (-webkit-min-device-pixel-ratio : 2){
      html{font-size: 37.5px;}
}

上面的這種方式在我以前作過的項目中使用了一段時間。上面的設置方法有一個很明顯的問題font-size是在一個屏幕寬度的區間上有一個基準值。像安卓手機種類的繁多,屏幕大小就更多的狀況下,上面的方法很雞肋。

第二種解決方案,就是使用JavaScript根據當前屏幕的寬度動態計算font-size值,這種方法能夠保證屏幕寬度連續變化的時候,font-size基準值也是連續變化的

計算方法能夠參考這篇文章《根據iPhone6設計稿動態計算rem值》

那麼最後一個問題也來了:爲何將計算rem單位的js放在head標籤裏面?

一句話總結:在瀏覽器中文檔流是從上往下加載渲染的。爲了保證發生沒必要要的重繪或者是重排確定是越早給根節點設置font-size值越好。

1.4什麼狀況下使用rem來佈局和注意的問題

  1. 總體的佈局仍是使用百分比

  2. 使用rem的最佳場景是,遇到例如多列帶有圖片的列表,經常須要圖片固定寬高比例

  3. 研究了一些網站,好比淘寶,對字體字體通常狀況建議使用px

  4. 出現1px像素線的地方,仍舊使用border-width:1px;而不是border-width:.1rem;

2.點5像素的由來

在前言中推薦閱讀的那篇文章中已經知道在 高清屏(Retina) 中控制顯示的最小的物理單元包括4個基本的像素點,而普通屏幕1個點像素就是1個物理像素單元。因此在高清屏(Retina)出來以前,就算咱們在css中寫 0.5px,對於顯示屏幕也是不識別的。可是在高清屏(Retina)中咱們能夠經過間接的方法實現0.5px的效果。

下面的這張圖可能能讓咱們迅速回憶上篇文章的內容

來看一段簡單的示例代碼加深理解:

<div class="item1"></div>
<div class="item2"></div>

css代碼以下:

.item1{
    width:100px;
    height:50px;
    border-top:1px solid #000;
    float: left;
    margin-top:10px;
}

.item2{
    width:100px;
    height:50px;
    margin-top:10px;
    border-top:.5px solid #de1dfb;
    float: left;
}

chrome中的顯示效果以下圖:

我把這張圖截取下來放到 PS 中放大能夠很明顯的看到一些問題。就是高清屏其實是用了兩排的物理像素點來顯示1px的像素線。且不作特殊處理的話1px0.5px的在Chrome瀏覽器中顯示效果是相同的。也就是說Chrome瀏覽器不識別0.5px

可是相同的代碼我在 safari瀏覽器中的效果倒是下面的效果:

下面的效果是我在PS中作了放大後的效果。

因此,咱們能夠得出一個結論:對於0.5px不一樣瀏覽器對它的識別是存在差別的。

如下這張圖是一位網友對一些經常使用設備是否識別0.5px作的統計:

其實從視覺的感覺上來講0.5px像素的顯示效果確實是比1px的顯示效果要好不少。大多數狀況下也更符合設計稿上的1px線的效果。那麼咱們有沒有辦法可讓1px在不一樣的瀏覽器和設備中顯示真正的1像素的效果呢?

答案是確定定的。

2.1傳統的實現方法,也就是咱們的項目中正在使用的方法:

僞元素 + css3的縮放巧妙地實現;

基本步驟就是:

  1. 設置目標元素定位參照

  2. 給目標元素添加一個僞元素before或者after,並設置絕對定位

  3. 給僞元素添上1px的邊框

  4. 設置僞元素的寬高爲目標元素的2倍

  5. 縮小0.5倍(變回目標元素的大小)

  6. 使用border-box把border包進來

先來看一個1像素0.5像素的顯示效果,下面的截圖是在chrome中:

實現的代碼以下:

<div class="item4">測試用的邊框</div>
<div class="item5">測試用的邊框</div>
.item4, .item5 {
    width: 200px;
    height: 100px;
    position: relative;
}

.item4 {
    border: 1px solid #000;
}

.item5::before {
    content: '';
    position: absolute;
    width: 200%;
    height: 200%;
    border: 1px solid #000;
    transform-origin: 0 0;
    transform: scale(0.5, 0.5);
    box-sizing: border-box;
}

能夠很明顯的看到縮放前和縮放後的效果。在ps中把上面的截圖放大不少倍之後咱們能夠看到顯示細節。縮放之後確實是使用的最小的物理像素單元來顯示邊框。以下圖:

注意:按照css3 transformscale的定義,理論上邊框能夠任意細(1/n px)。可是上圖中已是物理設備可以使用的最小的物理單元了,那麼咱們繼續縮放會有什麼現象呢?

使用一下代碼

<div class="item6">測試用的邊框</div>
.item6::before {
    content: '';
    position: absolute;
    width: 400%;
    height: 400%;
    border: 1px solid #000;
    transform-origin: 0 0;
    transform: scale(0.25, 0.25);
    box-sizing: border-box;
}

chrome中顯示效果以下,能夠很明顯的看到顏色變淺了,可是是否變得更細了咱們肉眼沒法分辨:

在PS中放大之後的效果:

很明顯能夠看到線並無變細,可是線的顏色確實是變淺了。這樣的結果也符合咱們的預期,就是已經到了物理設備可以顯示一塊顏色的最小的物理單元了。

2.2js動態設置viewport的方案

一些大廠(所謂的淘寶)的解決方案就是使用js動態獲取屏幕的設備像素比,而後動態設置viewport。固然也是我認爲目前最好的解決方案。

meta.setAttribute('content', 'initial-scale=' + 1/dpr + ', maximum-scale=' + 1/dpr + ', minimum-scale=' + 1/dpr + ', user-scalable=no');

咱們知道,通常咱們獲取到的視覺稿大部分是iphone6的,因此咱們看到的尺寸通常是雙倍大小的,在使用rem以前,咱們通常會自覺的將標註/2,其實這也並沒有道理,可是當咱們配合rem使用時,徹底能夠按照視覺稿上的尺寸來設置。

  1. 設計給的稿子雙倍的緣由是iphone6這種屏幕屬於高清屏,也便是設備像素比(device pixel ratio)dpr比較大,因此顯示的像素較爲清晰。

  2. 通常手機的dpr是1,iphone4,iphone5這種高清屏是2,iphone6s plus這種高清屏是3,能夠經過js的window.devicePixelRatio獲取到當前設備的dpr,因此iphone6給的視覺稿大小是(*2)750×1334了。

  3. 拿到了dpr以後,咱們就能夠在viewport meta頭裏,取消讓瀏覽器自動縮放頁面,而本身去設置viewport的content例如(這裏之因此要設置viewport是由於咱們要實現border1px的效果,在scale的影響下,高清屏中就會顯示成0.5px的效果)

總結:

由以上兩個部分的內容能夠知道,不論是在作多終端適配仍是實現點5像素的線,都是存在css和js兩種解決方案的。兩種方案相比來講,我都認爲使用JavaScript的解決方案都勝一籌。惟一的缺點就是會在html的head標籤中引入一段js代碼 。

記得剛入行的時候,業內有一個叫「雅虎軍規」的東西,是好多前端作頁面優化參考的標準。其中有一條就是要將js文件放在body標籤的底部。到如今不少年了,時代在變化,咱們的網絡帶寬也在提高,「雅虎軍規」中的一些內容,可能有些已經不適合咱們如今應用開發的場景。並且我覺的作技術不該該拘泥於已有的一些規定,而應該按照咱們的場景選擇適合咱們的技術和解決方案。

縱然有瑕疵,有些也是能夠經過技術手段來解決的。好比在head標籤中引入計算font-size和檢測設備獨立像素比的js的時候,會擔憂js的執行阻塞頁面的渲染。而咱們徹底能夠經過review的方式肯定js代碼的執行不會出現阻塞,而影響文檔流的加載。

仍是那句話:沒有十全十美的技術方案,只有適合不合適當前業務場景的技術方案。

參考文章

[css3的字體大小單位[rem]到低好在哪裏](https://www.zhihu.com/questio...
移動端高清、多屏適配方案
使用Flexible實現手淘H5頁面的終端適配
lib-flexible源代碼


相關文章
相關標籤/搜索