標籤:是你 mini element get remove 複雜 license 技術分享 資料
如今行動裝置的解析度紛繁複雜。以前僅僅是安卓機擁有各種各樣的適配問題,如今 iPhone 也擁有了三種主流的解析度,而未來的 iPhone 7 可能又會玩出什麼新花樣。如何以不變應萬變,用簡簡單單的幾行代碼就能支援種類繁多的螢幕解析度呢?今天就給大家介紹一種懶人必備的移動端定寬網頁適配方法。
首先看看下面這行代碼:
<meta name="viewport" content="width=device-width, user-scalabel=no">
有過移動端開發經驗的同學是不是對上面這句代碼非常熟悉?它可能最常見的響應式設計的 viewport 設定之一,而我今天介紹的這種方法也是利用了 meta 標籤設定 viewport 來支援大部分的移動端螢幕解析度。
目標
- 僅僅通過配置
<meta name="viewport"> 使得移動端網站只需要按照固定的寬度設計並實現,就能在任何主流的行動裝置上都能看到符合設計稿的頁面,包括 Android 4+、iPhone 4+。
測試裝置
- 三星 Note II (Android 4.1.2) - 真機
- 三星 Note III (Android 4.4.4 - API 19) - Genymotion 虛擬機器
- iPhone 6 (iOS 9.1) - 真機
iPhone
iPhone 的適配比較簡單,只需要設定 width 即可。比如:
<!-- for iPhone --><meta name="viewport" content="width=320, user-scalable=no" />
這樣你的頁面在所有的 iPhone 上,無論是 寬 375 像素的 iPhone 6 還是寬 414 像素的 iPhone 6 plus,都能顯示出定寬 320 像素的頁面。
Android
Android 上的適配被戲稱為移動端的 IE,確實存在著很多相容性問題。Android 以 4.4 版本為一個分水嶺,首先說一說相對好處理的 Android 4.4+
Android 4.4+
為了相容性考慮,Android 4.4 以上拋棄了 target-densitydpi 屬性,它只會在 Android 裝置上生效。如果對這個被廢棄的屬性感興趣,可以看看下面這兩個連結:
- Support for target-densitydpi is removed from WebKit
- Bug 88047 - Remove support for target-densitydpi in the viewport meta tag
我們可以像在 iPhone 上那樣設定 width=320 以達到我們想要的 320px 定寬的頁面設計。
<!-- for Android 4.4+ --><meta name="viewport" content="width=320, user-scalable=no" />
Android 4.0 ~ 4.3
作為 Android 相對較老的版本,它對 meta 中的 width 屬性支援得比較糟糕。以三星 Note II 為例,它的 device-width 是 360px。如果設定 viewport 中的 width (以下簡稱 vWidth ) 為小於等於 360 的值,則不會有任何作用;而設定 vWidth 為大於 360 的值,也不會使畫面產生縮放,而是出現了橫向捲軸。
想要對 Android 4.0 ~ 4.3 進行支援,還是不得不藉助於頁面縮放,以及那個被廢除的屬性:target-densitydpi。
target-densitydpi
target-densitydpi 一共有四種取值:low-dpi (0.75), medium-dpi (1.0), high-dpi (1.5), device-dpi。在 Android 4.0+ 的裝置中,device-dpi 一般都是 2.0。我使用手頭上的三星 Note II 裝置 (Android 4.1.2) 進行了一系列實驗,得到了下面這張表格:
| target-densitydpi |
viewport: width |
body width |
螢幕可視範圍寬度 |
| low-dpi (0.75) |
vWidth <= 320 |
270 |
270 |
| |
vWidth > 320 |
vWidth* |
270 |
| medium-dpi (1.0) |
vWidth <= 360 |
360 |
360 |
| |
vWidth > 360 |
vWidth* |
360 |
| high-dpi (1.5) |
vWidth <= 320 |
540 |
540 |
| |
320 < vWidth <= 540 |
vWidth* |
vWidth* |
| |
vWidth > 540 |
vWidth* |
540 |
| device-dpi (2.0)** |
vWidth <= 320 |
720 |
720 |
| |
320 < vWidth <= 720 |
vWidth* |
vWidth* |
| |
vWidth > 720 |
vWidth* |
720 |
- vWidth*:指的是與 viewport 中設定的 width 的值相同。
- device-dpi (2.0)**:在 Android 4.0+ 的裝置中,device-dpi 一般都是 2.0。
首先可以看到 320px 是個特別詭異的臨界值,低於這個臨界值後就會發生超出我們預期的事情。綜合考慮下來,還是採用 target-densitydpi = device-dpi 這一取值。如果你想要以 320px 作為頁面的寬度的話,我建議你針對安卓 4.4 以下的版本設定 width=321。
如果 body 的寬度超過螢幕可視範圍的寬度,就會出現水平的捲軸。這並不是我們期望的結果,所以我們還要用到縮放屬性 initial-scale。計算公式如下:
Scale = deviceWidth / vWidth
這樣的計算式不得不使用 JS 來實現,最終我們就能得到適配 Android 4.0 ~ 4.3 定寬的代碼:
var match, scale, TARGET_WIDTH = 320;if (match = navigator.userAgent.match(/Android (\d+\.\d+)/)) { if (parseFloat(match[1]) < 4.4) { if (TARGET_WIDTH == 320) TARGET_WIDTH++; var scale = window.screen.width / TARGET_WIDTH; document.querySelector(‘meta[name="viewport"]‘).setAttribute(‘content‘, ‘width=‘ + TARGET_WIDTH + ‘, initial-scale = ‘ + scale + ‘, target-densitydpi=device-dpi‘); }}
其中,TARGET_WIDTH 就是你所期望的寬度,注意這段代碼僅在 320-720px 之間有效哦。
縮放中的坑
如果是 iPhone 或者 Android 4.4+ 的機器,在使用 scale 相關的屬性時要非常謹慎,包括 initial-scale, maximum-scale 和 minimum-scale。 要麼保證 Scale = deviceWidth / vWidth,要麼就盡量不用。來看一個例子:
在縮放比不能保證的情況下,即使設定同樣的 width 和 initial-scale 後,兩者的表現也是不一致。具體兩種機型採用的策略如何我還沒有探索出來,有興趣的同學可以研究看看。最省事的辦法就是在 iPhone 和 Android 4.4+ 上不設定 scale 相關的屬性。
總結
結合上面所有的分析,你可以通過下面這段 JS 代碼來對所有 iPhone 和 Android 4+ 的手機螢幕進行適配:
var match, scale, TARGET_WIDTH = 320;if (match = navigator.userAgent.match(/Android (\d+\.\d+)/)) { if (parseFloat(match[1]) < 4.4) { if (TARGET_WIDTH == 320) TARGET_WIDTH++; var scale = window.screen.width / TARGET_WIDTH; document.querySelector(‘meta[name="viewport"]‘).setAttribute(‘content‘, ‘width=‘ + TARGET_WIDTH + ‘, initial-scale = ‘ + scale + ‘, target-densitydpi=device-dpi‘); }} else { document.querySelector(‘meta[name="viewport"]‘).setAttribute(‘content‘, ‘width=‘ + TARGET_WIDTH);}
如果你不希望你的頁面被使用者手動縮放,你還可以加上 user-scalable=no。不過需要注意的是,這個屬性在部分安卓機型上是無效的哦。
其他參考資料
- Supporting Different Screens in Web Apps - Android Developers
- Viewport target-densitydpi support is being deprecated
附錄 - 測試頁面
有興趣的同學可以拿這個測試頁面來測測自己的手機,別忘了改 viewport 哦。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=250, initial-scale=1.5, user-scalable=no"> <title>Document</title> <style> body { margin: 0; } div { background: #000; color: #fff; font-size: 30px; text-align: center; } .block { height: 50px; border-bottom: 4px solid #ccc; } #first { width: 100px; } #second { width: 200px; } #third { width: 300px; } #fourth { width: 320px; } #log { font-size: 16px; } </style></head><body> <div id="first" class="block">100px</div> <div id="second" class="block">200px</div> <div id="third" class="block">300px</div> <div id="fourth" class="block">320px</div> <div id="log"></div> <script> function log(content) { var logContainer = document.getElementById(‘log‘); var p = document.createElement(‘p‘); p.textContent = content; logContainer.appendChild(p); } log(‘body width:‘ + document.body.clientWidth) log(document.querySelector(‘[name="viewport"]‘).content) </script></body></html>
本文的著作權歸作者 鄒潤陽 所有,採用 Attribution-NonCommercial 3.0 License。任何人可以進行轉載、分享,但不可在未經允許的情況下用於商業用途;轉載請註明出處。感謝配合!
懶人必備的移動端定寬網頁適配方案