如果你看了很多篇文章,對 flexible 的解決方案一直還有一種不太瞭解的朦朧感覺,那麼這篇文章會幫你從背景角度一步一步解析爲何會有 flexible 這套解決方案,以及這套解決方案是如何作用的。
爲什麼不能直接用 px 進行佈局
設計師基於 iphone6(375px 邏輯像素) 的頁面尺寸給了你一個非常簡單的設計稿
你很快用如下的代碼實現了,在 chrome 控制檯打開 iphone6 查看效果,和設計稿絲毫不差。
<div class="box1"></div>
<div class="box2"></div>
<style>
.box1 {
width: 100px;
height: 100px;
border: 1px solid red;
position: fixed;
left: 10px;
}
.box2 {
width: 100px;
height: 100px;
border: 1px solid red;
position: fixed;
right: 10px;
}
</style>
但是選則機型爲 iphone5 時頁面變成了這個樣子。
會發現在 iphone5 的頁面下兩個方塊的間距和方塊大小比起來變得非常小,這樣我們認爲這個網頁在不同屏幕尺寸的手機上產生了不同的顯示效果。
這個時候肯定有同學說了,那這個設計圖我用 vw 去還原就好了。的確使用 vw 還原設計圖是可以做到的,但 vw 目前的兼容性仍然有問題,測試很可能甩過來一個 vivo-x7 告訴你頁面亂掉了。
Flexible 是如何解決以上問題的
設計師在 375px ( iphone6 邏輯像素寬度 ) 的頁面上畫出了這個設計圖,則該 100px 的方塊在不同尺寸的手機上的大小實際應該爲 (100px/375px)*{屏幕邏輯像素寬度}
,然而我們不可能爲每一種尺寸都在 css 上寫一個 px 寬度。這時可以使用 rem,我們可以通過設置根節點的 font-size 來使不同尺寸的手機的 1rem 對應的 px 值不同。
上面的話讀起來很繞,我寫起來很繞,那麼我將 100px 在 iphone5 和 iphone6 上的不同數值列個表
手機型號 | 邏輯像素寬度 | 根節點 font-size | rem 數值 | px 數值 |
---|---|---|---|---|
iphone5 | 320 px | 64 px | (100/75) rem | (100/75)*64 px |
iphone6 | 375 px | 75 px | (100/75) rem | (100/75)*75 px |
這樣在 iphone6 的屏幕尺寸上以 100px 渲染出來的盒子大小才能在 iphone5 的屏幕尺寸上以相同的比例渲染出來,在 iphone5 上渲染寬度爲 (100/75)*64px == 85.33px
,所以 flexible 的源碼中refreshRem
函數就是在計算不同手機尺寸應當設置的根節點 font-size
,如下所示。
function refreshRem() {
var width = docEl.getBoundingClientRect().width
if (width / dpr > 540) {
// 這裏是爲了適配 ipad 和 android 平板橫屏的情況(請暫時無視這個 if 語句)
width = 540 * dpr
}
var rem = width / 10 // 將屏幕寬度分爲 10 份,每份爲 1rem
docEl.style.fontSize = rem + 'px'
flexible.rem = win.rem = rem
}
如何解決 1px 像素問題
如果讀這篇文章同學在之前有讀過其他地方的文章會認爲我上面的解釋是錯誤的,因爲其他的文章在上面均使用的屏幕物理像素
作爲基準,並配合設置 <viewport content="width=device-width,user-scalable=no,initial-scale=${scale}">
實現,其實,如果沒有 1px 像素的問題是不需要設置 initial-scale
的。在文章的前半部分着重講解了如何用 rem 實現類似 vw 的效果,下面是關於 1px
像素問題的來龍去脈。
什麼是 1px 像素問題
首先需要區分 物理像素 和 邏輯像素,物理像素即爲顯示器上的最小顯示單元,邏輯像素也就是 css 像素,是在網頁上渲染時的最小單位。而物理像素與邏輯像素之比 (物理像素/邏輯像素) 稱之爲 dpr (device pixel ratio),iphone 5
的物理像素寬度爲 640px,邏輯像素寬度爲 320px,則 iphone5 的 dpr 爲 2。意味着在 css 上寫 1px 的寬度,在 iphone5 的顯示屏幕上實際渲染了 2px 的物理像素,使得我們在 dpr>=2
的手機上設置 border-width:1px
看起來非常的粗。
如何解決 1px 像素問題
爲了解決這個問題,flexible
對上面的方案進行了優化。將 initial-scale
設置爲 1/dpr
,這樣 dpr 爲 2 的手機就會將頁面縮小至 1/2 顯示,這個時候設置 border-width:1px
就真的是 1px 物理像素。但是不能因爲這個修改影響其他地方的佈局,其實解決方案也很簡單,就是將根節點的 font-size = font-size * dpr
,來將佈局大小放大回正常的樣子(但實際不是這樣寫的,是因爲當設置了 scale = 1/2 時,documentn.documentElement.getBoundingClientRect().width
取得邏輯像素值會增大至 2 倍,所以不需要額外進行 根節點的 font-size = font-size * dpr
)。
var width = docEl.getBoundingClientRect().width
var rem = width / 10 // 將屏幕寬度分爲 10 份,每份爲 1rem
docEl.style.fontSize = rem + 'px'
字體不能使用 rem,可能會出現小數
在 flexible 中有這樣一行代碼 docEl.setAttribute('data-dpr', dpr);
這樣我們可以在自己的 css 中根據data-dpr
來設置不同 dpr (可以理解爲頁面縮放比例) 下的字體大小
[data-dpr='1'] .text{
font-size: 12px;
}
[data-dpr='2'] .text{
font-size: 24px;
}
[data-dpr='3'] .text{
font-size: 36px;
}
上面設計圖的正確還原方式
在你的瀏覽器中打開這段代碼並在移動端不同機型上進行切換,會得到正確的設計圖還原效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script type="text/javascript" src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.2/??flexible_css.js,flexible.js">
</script>
<title>Document</title>
</head>
<body>
<div class="box1"></div>
<div class="box2"></div>
<style>
.box1 {
width: 2.666666666666667rem; // 100px ÷ 37.5px(375px設計稿上的根 font-size 大小) = 2.666666666666667rem ,使用 css 預處理器的可以寫一個函數
height: 2.666666666666667rem;
border: 1px solid red;
position: fixed;
left: 0.2666666666666667rem;
top: 0.2666666666666667rem;
}
.box2 {
width: 2.666666666666667rem;
height: 2.666666666666667rem;
border: 1px solid red;
position: fixed;
right: 0.2666666666666667rem;
top: 0.2666666666666667rem;
}
</style>
</body>
</html>
Flexible 中源碼的其他部分
希望看完這篇文章的你,再去這裏再好好看一下源碼的實現細節部分:flexible 源碼