移动端用户设置字体放大导致的问题
來源地址
來源作者
問題背景
很多webview提供了調整頁面字體大小的功能,例如手機QQ、微信、部分Android內置瀏覽器等。大部分瀏覽器調整字體只會導致字體顯示大小發生改變,其他元素的大小不受影響。但對于結構稍微復雜一點的頁面,字體大小的變動就足以導致頁面布局亂掉,導致文本不居中、文字折行、布局混亂等問題。
調整字體大小功能24-mobile-browser-font-size/1.png)
作為前端工程師,碰到頁面亂掉的情況就會覺得很無辜了,明明是你自己放大的字體,放大了卻還要我來承擔排版亂掉的后果,多委屈啊。很久以前,在PC端,好歹我們還可以提示用戶按CTLR + 0將頁面的比例調整回來,現在在移動端,卻很難阻止用戶縮放字體大小。
然而即使再委屈,當問題來了之后還是需要處理的,誰讓我們是前端工程師呢?(笑)
原理
由于并不是很清楚各個平臺(瀏覽器)放大字體的機制,我分別咨詢了我們 iOS 和 Android 的同事,得知在調整字體大小時時,2個客戶端的處理方式不同。
iOS
iOS上需要調整 webview 的字體大小時,是通過給 body 設置 ??-webkit-text-size-adjust?? 屬性實現的:iOS設置字體縮放代碼。既然這樣,我們應該可以通過JS取到這個屬性:
var body = document.body; alert(body.getAttribute('style'));iOS獲取樣式
當頁面文字被放大時,確實多了一個??-webkit-text-size-adjust??屬性。
Android
Android通過給 webview 設置字體的縮放來完成,具體的API是??setTextZoom(int)??。Android設置字體縮放代碼。Android設置字體縮放代碼,文字被放大了。例如“文字大小10px”這一段文字被放大了兩倍,隨文字一同被放大的還有以??em???為單位的尺寸和??line-height??。
于是很自然地想到,我們是否可以取到這些屬性呢?
按iOS的方式,也取不到任何有用的樣式,可見Android webview中并不是使用??-webkit-text-size-adjust??這個屬性來放大文字的。
一籌莫展之際,忽然想到是否應該取一下??computedStyle???
window.getComputedStyle(document.querySelector('.fs10'),null).getPropertyValue('font-size')這次終于有結果了,“文字大小10px”這一段文字明明白白地被使用了??20px??的文字大小!
至此,我們可以大概推測出 Android webview 放大文字的原理:在CSS解析之后,渲染之前,將所有的字體大小的值進行縮放,后面的排版和渲染都會直接使用縮放后的CSS值。
解決方案
ios
針對iOS,調整字體大小本身只是改變??body??的css屬性,因此可以通過覆蓋樣式來控制。
body {-webkit-text-size-adjust: 100% !important; }Android
Android因為改變的是字體的大小,所以可以考慮將字體大小在設置的時候進行等比例縮小。例如,一個文字希望以??10px???來進行渲染,當webview被放大兩倍時,此時??font-size???會變為??20px???。因此我們可以在取到這個放大比例之后,對原樣式進行等比縮小,比如將原文字大小設置為??5px???,渲染的時候就變成了??10px??。
var $dom = document.querySelector('.fs10'); var originFontSize = 10; var scaledFontSize = parseInt(window.getComputedStyle($dom, null).getPropertyValue('font-size')); var scaleFactor = originFontSize / scaledFontSize; $dom.style.fontSize = originFontSize * scaleFactor;但是這樣做仍然有幾個問題:
一次只能操作一個DOM元素,無法批量處理
需要知道DOM元素原來設置的字體大小
這幾個問題并不如想象中的好解決。于是另辟蹊徑,看看是否有一勞永逸的辦法。腦海中很快冒出一個名詞——??rem??!
如果我們的頁面字體大小都使用??rem???進行聲明,那么我們就只需要在頁面加載的時候根據縮放比例計算出??html??元素的字體大小即可!詳見下方代碼:
(function(){var $dom = document.createElement('div');$dom.style = 'font-size:10px;';document.body.appendChild($dom);// 計算出放大后的字體var scaledFontSize = parseInt(window.getComputedStyle($dom, null).getPropertyValue('font-size'));document.body.removeChild($dom);// 計算原字體和放大后字體的比例var scaleFactor = 10 / scaledFontSize;// 取html元素的字體大小// 注意,這個大小也經過縮放了// 所以下方計算的時候 *scaledFontSize是原來的html字體大小// 再次 *scaledFontSize才是我們要設置的大小var originRootFontSize = parseInt(window.getComputedStyle(document.documentElement, null).getPropertyValue('font-size'));document.documentElement.style.fontSize = originRootFontSize * scaleFactor * scaleFactor + 'px'; })();因為這段代碼中創建了一個元素,并放入了??document.body???中,所以不能放在??head???中運行。如果放在頁尾運行的話,則有可能會產生閃爍的情況,因此最好的辦法是將這段代碼放在????開始的地方。
除了在Android webview以外,以上代碼在 Android 微信中實測也有效。
其它方案
Android微信
在編寫本文時,通過網上一些資料,發現在Android微信中,也可以借助??WeixinJSBridge??對象來阻止字體大小調整。實測也有效。
(function() {if (typeof WeixinJSBridge == "object" && typeof WeixinJSBridge.invoke == "function") {handleFontSize();} else {document.addEventListener("WeixinJSBridgeReady", handleFontSize, false);}function handleFontSize() {// 設置網頁字體為默認大小WeixinJSBridge.invoke('setFontSizeCallback', { 'fontSize' : 0 });// 重寫設置網頁字體大小的事件WeixinJSBridge.on('menu:setfont', function() {WeixinJSBridge.invoke('setFontSizeCallback', { 'fontSize' : 0 });});}})();Android QQ
作為用戶量龐大的APP之一,QQ也提供了禁止調整字體大小的方案,android qq中可以自定義webview顯示的控件,通過在url中加入指定參數即可。見? ?如何定制手Q的Webview??.
手機QQ文檔
理論上,www.futu5.com/?_wv=128訪問這個鏈接,功能菜單中不會出現調整字體大小的按鈕。但是,但是,但是,在我實測過程中,所有的參數中,就只有【128隱藏字體項不生效】。不知道是QQ的bug還是有意為之,目前已提交反饋,但未收到回應。
聲音和思考
在組內分享的時候,大家對于字體大小調整這個頭疼的問題各自有不同的看法,大概有怎么幾種聲音:
從產品的角度來說,微信、QQ等客戶端既然提供調整字體的功能,必然是想用它來提供更好的體驗,不應該禁用。
從開發的角度來說,字體縮放之后,頁面會亂掉,根本原因在于頁面的適應性不夠,應該從代碼層面去優化。
繼續從開發的角度說,雖然理論上開發應該做好適配,但是對于文字突然被放大兩倍,很多時候確實心有余而力不足。如果要做好,需要花費大量的時間和精力,并且需要設計和產品同學從設計上留出一些適配空間。
既然用戶選擇了用大字體來瀏覽頁面,他就應該知道這個頁面是被自己放大了,需要承擔頁面布局亂掉的結果。
?一、用戶修改手機字體設置大小,影響App里打開的web頁面。?
手機字體設置大小,影響App的頁面。
Android的可以通過webview配置webview.getSettings().setTextZoom(100)就可以禁止縮放,按照百分百顯示。
?二、用戶調整瀏覽器字體大小,影響的是從瀏覽器打開的web頁?
瀏覽器設置字體大小,影響瀏覽器打開的頁面。通過js可控制用戶修改字體大小,使頁面不受影響。
(function(doc, win) { // 用原生方法獲取用戶設置的瀏覽器的字體大小(兼容ie)if(doc.documentElement.currentStyle) {var user_webset_font=doc.documentElement.currentStyle['fontSize'];}else {var user_webset_font=getComputedStyle(doc.documentElement,false)['fontSize'];} // 取整后與默認16px的比例系數var xs=parseFloat(user_webset_font)/16; // 設置rem的js設置的字體大小var view_jsset_font,result_font;var docEl = doc.documentElement,resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',clientWidth,recalc = function() {clientWidth = docEl.clientWidth;if(!clientWidth) return;if(!doc.addEventListener) return;if(clientWidth<750){ // 設置rem的js設置的字體大小view_jsset_font=100 * (clientWidth / 750); // 最終的字體大小為rem字體/系數result_font=view_jsset_font/xs; // 設置根字體大小docEl.style.fontSize = result_font + 'px';}else{docEl.style.fontSize = 100 + 'px';}};win.addEventListener(resizeEvt, recalc, false);doc.addEventListener('DOMContentLoaded', recalc, false); })(document, window);總結
以上是生活随笔為你收集整理的移动端用户设置字体放大导致的问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SVM和SVR简介
- 下一篇: 计算机研究所专业课,【择校必看】十三所计