盤點移動端適配方案
作為開發(fā)者柠横,在手機(jī)移動端做適配的時候會出現(xiàn)很多問題:最不希望用戶看到的就是橫向滾動條笼痛。其次是文字(圖片等)的大小不能一成不變祈秕,要根據(jù)用戶設(shè)備的物理像素調(diào)節(jié)大小唯蝶。
手機(jī)瀏覽器是把頁面放在一個虛擬的"窗口"(viewport)中,通常這個虛擬的"窗口"(viewport)比屏幕寬详瑞,這樣就不用把每個網(wǎng)頁擠到很小的窗口中(<u>這樣會破壞沒有針對手機(jī)瀏覽器優(yōu)化的網(wǎng)頁的布局</u>)掂林,用戶可以通過平移和縮放來看網(wǎng)頁的不同部分。
這就該輪到meta標(biāo)簽出場了坝橡。meta標(biāo)簽的作用是讓當(dāng)前viewport的寬度等于設(shè)備的寬度泻帮,同時不允許用戶手動縮放。也許允不允許用戶縮放计寇,不同的網(wǎng)站有不同的要求锣杂,但讓viewport的寬度等于設(shè)備的寬度,這個應(yīng)該是大家都想要的效果番宁,如果你不這樣的設(shè)定的話元莫,就會使用那個比屏幕寬的默認(rèn)viewport,也就是說會出現(xiàn)橫向滾動條贝淤;
- 設(shè)置Viewport
一個常用的針對移動網(wǎng)頁優(yōu)化過的頁面的 viewport meta 標(biāo)簽大致如下:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- width
控制 viewport 的大小柒竞,可以指定的一個值,如 600播聪,或者特殊的值,如 device-width 為設(shè)備的寬度(單位為縮放為 100% 時的 CSS 的像素)布隔。
- initial-scale
初始縮放比例离陶,也即是當(dāng)頁面第一次 load 的時候縮放比例。
- user-scalable
用戶是否可以手動縮放(默認(rèn)設(shè)置為no衅檀,因為我們不希望用戶放大縮小頁面)
- minimum-scale
允許用戶縮放到的最小比例(默認(rèn)設(shè)置為1.0)
- maximum-scale
允許用戶縮放到的最大比例(默認(rèn)設(shè)置為1.0)
一招刨、rem適配
rem(font size of the root element)是指相對于根元素的字體大小的單位。簡單的說它就是一個相對單位哀军〕量簦看到rem大家一定會想起em單位,em(font size of the element)是指相對于父元素的字體大小的單位杉适。它們之間其實很相似谎倔,只不過一個計算的規(guī)則是依賴根元素一個是依賴父元素計算。
正是因為它適配的標(biāo)準(zhǔn)是根元素的字體大小猿推,而不同的手機(jī)型號對于根元素的規(guī)定不同[1]捌肴,這便增加了很多不必要的判斷。
二状知、vw適配
vw(viewpoint width),即視窗寬度孽查,1vw等于視窗寬度的1%
計算方式(以750的設(shè)計稿為準(zhǔn)):設(shè)計圖中元素固定的大小(px) * 100 / 750 => vw
eg : 90px轉(zhuǎn)化為vw : 90 * 100 / 750 => 12vw
如果是375的設(shè)計稿需要乘以2 : 設(shè)計圖中元素固定的大小(px) * 2 * 100 / 750 => vw
不難看出,使用的時候仍需進(jìn)行大量的計算盲再。
三、vw+rem適配
因為vw的比例需要動態(tài)地計算洲胖,而rem做移動端布局的時候剛好需要動態(tài)地改變,因此我們只要稍加計算绿映,將根元素的字體大小換成vw即可擒滑。
-
640的設(shè)計稿
320px == 100vw ; 1px == 100 / 320 == 0.3125vw ; 100px == 31.25vw;
此時設(shè)置根元素字體大小
html,body{font-size:31.25vw}
, => 1rem == 100px; =>1px == 0.01rem;所以丐一,根據(jù)設(shè)計稿的像素計算時,只需將px除以100即可
計算方式:元素尺寸 / 2 / 100 = rem
320的設(shè)計稿計算方式:元素尺寸 / 100 = rem
-
750的設(shè)計稿
375px == 100vw ; 1px == 100 / 375 == 0.26666vw ; 100px == 26.6666vw
出現(xiàn)了小數(shù)點后除不盡的情況……為了將結(jié)果它轉(zhuǎn)換成整數(shù) 120px == 32vw此時設(shè)置根元素字體大小
html,body{font-size:32vw}
, 計算方式也發(fā)生改變:元素尺寸 / 2 / 120 = rem375的設(shè)計稿計算方式:元素尺寸 / 120 = rem
以上面的幾種設(shè)計稿為例淹冰,尤其是750的設(shè)計稿居多库车,大多數(shù)情況下根據(jù)元素的尺寸 / 120 ,這么難以計算的數(shù)字樱拴,還是交給插件來做吧柠衍。以VScode中的cssrem為例:
注意:配置完參數(shù)之后,重啟軟件
四晶乔、flexible.js布局(推薦)
通過flexible.js實現(xiàn)了rem自適應(yīng)珍坊,有了flexible.js,我們就不必再為移動端各種設(shè)備兼容煩惱正罢。通過rem與px的換算阵漏,把設(shè)計稿從px轉(zhuǎn)到rem。再也不用為各種設(shè)備橫行而擔(dān)憂翻具。
rem是相對于根元素html履怯,這樣就意味著,我們只需要在根元素確定一個px字號裆泳,則可以來算出元素的寬高叹洲。1rem=16px(瀏覽器html的像素,可以設(shè)定這個基準(zhǔn)值)晾虑,假如瀏覽器的html設(shè)為64px疹味,則下面的元素則1rem=64px來運(yùn)算仅叫。
阿里團(tuán)隊開源的一個庫。使用flexible.js輕松搞定各種不同的移動端設(shè)備兼容自適應(yīng)問題糙捺。
-
刪除viewport的meta標(biāo)簽诫咱,替換為下面的JS代碼
(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('將根據(jù)已有的meta標(biāo)簽來設(shè)置縮放比例'); 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 { // 其他設(shè)備下,仍舊使用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 > 768) { width = 768 * dpr; } var rem = width / 7.5; 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); 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'] = {}));
以750設(shè)計稿為準(zhǔn)签钩,元素尺寸 / 100 = rem ; 以375設(shè)計稿為準(zhǔn)掏呼,元素尺寸 * 2 / 100 = rem ,因為flexble中會再除以2铅檩,所以這里乘以2將其抵消憎夷。
-
根字體大小:iPhone5s:12px iPhone6S:14px iPhone6P:16px ?