摘要:但是有一部分的安卓機(jī),并不等于根節(jié)點(diǎn)的,舉個(gè)例子的,正常情況下也應(yīng)該是,但在部分機(jī)型中,它可能是或等等筆者懷疑上文中提到的頁面寬度溢出也是這個(gè)問題。
前言
移動(dòng)端的崛起,給了我們前端更大的舞臺(tái),與此同時(shí),也給我們帶來了一系列頭疼的問題,移動(dòng)端適配就是其中之一,目前市面上最常用的方案即是REM適配。
為什么說她是一個(gè)磨人的小妖精?因?yàn)樗_實(shí)讓人又愛又恨,靈活的自適應(yīng)布局再搭配上css單位轉(zhuǎn)換工具,讓人愛不釋手;另一方面,由于移動(dòng)端的機(jī)型和表現(xiàn)千奇百怪,想要達(dá)到完美的兼容又讓人頭疼。
即使如此,依然阻止不了筆者對(duì)于她的癡迷。本文將會(huì)圍繞REM適配這一話題進(jìn)行討論,同時(shí)也會(huì)將筆者個(gè)人的經(jīng)驗(yàn)以及自己目前在用的一套代碼分享給大家。另外,如今移動(dòng)端的兼容性越來越好,因此衍生出了一些其他的適配方案,這點(diǎn)不在本文的討論范圍之內(nèi)。
實(shí)例解析 全局變量const docEl = document.documentElement const metaEl = document.querySelector("meta[name="viewport"]") const maxWidth = window.__MAX_WIDTH__ || 750 const divPart = window.__DIV_PART__ || 15 const bodySize = window.__BODY_SIZE__ || 12 let scale = 1 let dpr = 1 let timer = null
metaEl:抓取現(xiàn)有viewport,以支持使用者自定義頁面實(shí)際縮放比例,通過設(shè)置viewport可以實(shí)現(xiàn)視覺上的實(shí)際物理像素。例如initial-scale=0.5,即二倍屏,假設(shè)根節(jié)點(diǎn)的font-size=100px,那么0.01rem就是物理像素1px;而initial-scale=1.0,雖然在css單位中,0.01rem=1px,但我們知道,在二倍屏中,1px實(shí)際有4個(gè)物理像素。
maxWidth:UI稿寬度,一般以iphone6為基準(zhǔn),即750。
divPart:將設(shè)備寬度劃分為多少份,上述代碼中,750/15=50,意思是750寬度的屏幕,1rem=50px,劃分多少份實(shí)際上沒有固定規(guī)定,看個(gè)人習(xí)慣。
bodySize:初始化時(shí),設(shè)置body的字體大小。
scale、dpr分別是頁面縮放比例、設(shè)備像素比。
初始化設(shè)置if (metaEl) { console.warn("根據(jù)已有的meta標(biāo)簽來設(shè)置縮放比例") const match = metaEl.getAttribute("content").match(/initial-scale=([d.]+)/) if (match) { scale = parseFloat(match[1]) dpr = parseInt(1 / scale) } } else { if (window.navigator.appVersion.match(/iphone/gi)) { dpr = parseInt(window.devicePixelRatio) || 1 scale = 1 / dpr } const newMetaEl = document.createElement("meta") newMetaEl.setAttribute("name", "viewport") newMetaEl.setAttribute("content", `width=device-width, initial-scale=${scale}, maximum-scale=${scale}, minimum-scale=${scale}, user-scalable=no`) docEl.firstElementChild.appendChild(newMetaEl) } // 設(shè)置根節(jié)點(diǎn)dpr docEl.setAttribute("data-dpr", dpr)
這里要重點(diǎn)將一下為什么要區(qū)分安卓和IOS設(shè)備,很多人可能會(huì)說因?yàn)镮OS有多倍屏。實(shí)際上,安卓也有多倍屏,那為什么我們不考慮呢?
有些安卓機(jī)的設(shè)備像素比很奇怪,比如2.5、3.8等一些奇怪的數(shù)字;
部分安卓機(jī)表現(xiàn)很奇怪,比如頁面寬度比屏幕寬度多一點(diǎn),出現(xiàn)橫向滾動(dòng)條(具體原因不詳,已排除所有css干擾),兼容起來成本太高。
核心代碼function bodyLoaded (cb) { if (document.body) { cb && cb() } else { document.addEventListener("DOMContentLoaded", function () { cb && cb() }, false) } } // 窗口寬度改變時(shí),刷新rem function refreshRem () { let width = docEl.clientWidth if (width / dpr > maxWidth) { width = maxWidth * dpr } // 設(shè)置根節(jié)點(diǎn)font-size window.remUnit = width / divPart docEl.style.fontSize = window.remUnit + "px" bodyLoaded(() => { // 測試rem的準(zhǔn)確性,如果和預(yù)期不一樣,則進(jìn)行縮放 let noEl = document.createElement("div") noEl.style.width = "1rem" noEl.style.height = "0" document.body.appendChild(noEl) let rate = noEl.clientWidth / window.remUnit if (Math.abs(rate - 1) >= 0.01) { docEl.style.fontSize = (window.remUnit / rate) + "px" } document.body.removeChild(noEl) }) } // 初始化 refreshRem() bodyLoaded(() => { document.body.style.fontSize = bodySize * dpr + "px" document.body.style.maxWidth = maxWidth * dpr + "px" })
refreshRem函數(shù)是整個(gè)rem適配的核心,每次需要更新都會(huì)調(diào)用此函數(shù),我們還限定了頁面的最大寬度,可以保證在pc端打開也能看到不錯(cuò)的視覺效果。
但是有一部分的安卓機(jī),1rem并不等于根節(jié)點(diǎn)的font-size,舉個(gè)例子:html的font-size=20px,正常情況下1rem也應(yīng)該是20px,但在部分機(jī)型中,它可能是22px或18px等等(筆者懷疑上文中提到的頁面寬度溢出也是這個(gè)問題)。因此,筆者加上了bodyLoaded這段代碼,在rem設(shè)置完成后,再與實(shí)際視覺上的1rem進(jìn)行比較,若偏差超過1%,則認(rèn)為需要重新定義rem,這樣就能100%保證1rem就是我們期望的大小。
頁面寬度監(jiān)聽window.addEventListener("resize", function () { clearTimeout(timer) timer = setTimeout(refreshRem, 200) }, false) // window.addEventListener("pageshow", function (e) { // if (e.persisted) { // refreshRem() // } // }, false)
這段代碼用于監(jiān)聽resize事件,以此來重新計(jì)算根節(jié)點(diǎn)的font-size,定時(shí)器用來防止頻繁計(jì)算(實(shí)際上在手機(jī)中,也不會(huì)有頻繁觸發(fā)resize的機(jī)會(huì),因此定時(shí)器也可以不加)。有些讀者可能會(huì)問題,為什么不監(jiān)聽橫豎屏事件(onorientationchange),其實(shí)沒有必要,橫豎屏切換本質(zhì)也是resize的一種,我們已經(jīng)監(jiān)聽了resize事件,這里就沒有必要再次監(jiān)聽了。
那注釋掉的這段代碼是什么意思呢?它是用來監(jiān)聽瀏覽器返回,但是這段代碼在iPhone8、iPhoneX上會(huì)有問題,在返回的時(shí)候,我們拿到的document.documentElement.clientWidth是其實(shí)際的大小(沒有乘上設(shè)備像素比),因此整個(gè)頁面布局都亂了。筆者經(jīng)過深思熟慮,決定刪掉這段代碼,因?yàn)樵诜祷氐臅r(shí)候,會(huì)保留和離開時(shí)一摸一樣的狀態(tài),沒有必要重新再計(jì)算一遍。
工具函數(shù)window.px2rem = function (d) { let val = parseFloat(d) / window.remUnit if (typeof d === "string" && d.match(/px$/)) { val += "rem" } return val } window.rem2px = function (d) { let val = parseFloat(d) * window.remUnit if (typeof d === "string" && d.match(/rem$/)) { val += "px" } return val }
暴露全局函數(shù),方便使用js來控制尺寸大小。
CSS重置樣式篇幅所限,樣式代碼就不在這里貼了,感興趣可以在這里看:reset.css
總結(jié)這一套rem適配代碼是筆者日常開發(fā)中總結(jié)提煉出來,不能說是100%完美,但是也足夠適配市面上的主流機(jī)型了。再配合構(gòu)建工具,自動(dòng)轉(zhuǎn)換為rem單位,省心又省力。
最后推薦一個(gè)好用的全局構(gòu)建工具fle-cli,幫你從復(fù)雜繁瑣的構(gòu)建配置中解放出來。
本文源碼地址:https://github.com/ansenhuang/axe/blob/master/packages/rem-resize/README.md
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/95529.html
摘要:這是我第一次接觸微信支付,發(fā)現(xiàn)網(wǎng)上還是有很多同學(xué)在求助,了怎么辦是什么情況為了幫助更多的小伙伴脫離苦海,我決定寫下這次的踩坑之旅,給更多的人幫助。 凡是和錢打交道的事,沒有一樣是容易的。這是我第一次接觸微信支付,發(fā)現(xiàn)網(wǎng)上還是有很多同學(xué)在求助,XXX了怎么辦?XXX是什么情況?為了幫助更多的小伙伴脫離苦海,我決定寫下這次的踩坑之旅,給更多的人幫助。 介紹 微信支付方式分為刷卡支付、公眾號(hào)...
摘要:這是我第一次接觸微信支付,發(fā)現(xiàn)網(wǎng)上還是有很多同學(xué)在求助,了怎么辦是什么情況為了幫助更多的小伙伴脫離苦海,我決定寫下這次的踩坑之旅,給更多的人幫助。 凡是和錢打交道的事,沒有一樣是容易的。這是我第一次接觸微信支付,發(fā)現(xiàn)網(wǎng)上還是有很多同學(xué)在求助,XXX了怎么辦?XXX是什么情況?為了幫助更多的小伙伴脫離苦海,我決定寫下這次的踩坑之旅,給更多的人幫助。 介紹 微信支付方式分為刷卡支付、公眾號(hào)...
摘要:你的線上代碼真的沒有嗎歡迎免費(fèi)使用我們可以幫助您第一時(shí)間發(fā)現(xiàn)字符串拼接加法仔細(xì)查看生成的代碼,你會(huì)發(fā)現(xiàn)出現(xiàn)在標(biāo)記的后面,然而標(biāo)簽不見了。在中,根據(jù)左右兩邊變量的類型的不同,符號(hào)可以用于數(shù)字相加或則字符串拼接。然后又轉(zhuǎn)換為字符串拼接起來。 譯者按: bug雖小,卻是個(gè)磨人的小妖精! 原文: Fixing a bug: when concatenated strings turn int...
摘要:相比于傳統(tǒng)的是一種現(xiàn)代的為準(zhǔn)備的優(yōu)質(zhì)替代方案??傊且环N的替代方案。一般化的樣式為大部分元素提供。保護(hù)了有價(jià)值的默認(rèn)值通過為幾乎所有的元素施加默認(rèn)樣式,強(qiáng)行使得元素有相同的視覺效果。 Normalize.css只是一個(gè)很小的css文件,但它在磨人的HTML元素樣式上提供了跨瀏覽器的高度一致性。相比于傳統(tǒng)的CSS reset,Normalize.css是一種現(xiàn)代的、為HTML5準(zhǔn)備的優(yōu)...
閱讀 3713·2021-11-11 16:55
閱讀 1655·2021-10-08 10:04
閱讀 3591·2021-09-27 13:36
閱讀 2785·2019-08-30 15:53
閱讀 1870·2019-08-30 11:17
閱讀 1272·2019-08-29 16:55
閱讀 2111·2019-08-29 13:57
閱讀 2526·2019-08-29 13:13