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