移動端用戶設置字體放大導致的問題


問題背景

很多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設置字體縮放代碼iOS設置字體縮放代碼

 

既然這樣,我們應該可以通過JS取到這個屬性:

var body = document.body; alert(body.getAttribute('style'));

 

iOS獲取樣式iOS獲取樣式

 

圖上可以看到,當頁面文字被放大時,確實多了一個-webkit-text-size-adjust屬性。

Android

Android通過給 webview 設置字體的縮放來完成,具體的API是setTextZoom(int)

 

Android設置字體縮放代碼Android設置字體縮放代碼

 

我們通過一個demo頁面來查看效果:

 

Android設置字體縮放代碼Android設置字體縮放代碼

 

從圖中可以看到,文字確實是被放大了。例如“文字大小10px”這一段文字被放大了兩倍,隨文字一同被放大的還有以em為單位的尺寸和line-height

於是很自然地想到,我們是否可以取到這些屬性呢?

// 取元素的fontSize document.querySelector('.s10').style.fontSize;

結果很失望,取不到什么有用的信息。

按iOS的方式,也取不到任何有用的樣式,可見Android webview中並不是使用-webkit-text-size-adjust這個屬性來放大文字的。

一籌莫展之際,忽然想到是否應該取一下computedStyle

window.getComputedStyle(document.querySelector('.fs10'),null).getPropertyValue('font-size')

這次終於有結果了,“文字大小10px”這一段文字明明白白地被使用了20px的文字大小!

至此,我們可以大概推測出 Android webview 放大文字的原理:在CSS解析之后,渲染之前,將所有的字體大小的值進行縮放,后面的排版和渲染都會直接使用縮放后的CSS值。

解決方案

針對iOS,調整字體大小本身只是改變body的css屬性,因此可以通過覆蓋樣式來控制。

body {
    -webkit-text-size-adjust: 100% !important;
}

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;

但是這樣做仍然有幾個問題:

  1. 一次只能操作一個DOM元素,無法批量處理
  2. 需要知道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中運行。如果放在頁尾運行的話,則有可能會產生閃爍的情況,因此最好的辦法是將這段代碼放在<body>開始的地方。

除了在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文檔手機QQ文檔

 

理論上,www.futu5.com/?_wv=128訪問這個鏈接,功能菜單中不會出現調整字體大小的按鈕。但是,但是,但是,在我實測過程中,所有的參數中,就只有【128隱藏字體項不生效】。不知道是QQ的bug還是有意為之,目前已提交反饋,但未收到回應。

聲音和思考

在組內分享的時候,大家對於字體大小調整這個頭疼的問題各自有不同的看法,大概有怎么幾種聲音:

  1. 從產品的角度來說,微信、QQ等客戶端既然提供調整字體的功能,必然是想用它來提供更好的體驗,不應該禁用。
  2. 從開發的角度來說,字體縮放之后,頁面會亂掉,根本原因在於頁面的適應性不夠,應該從代碼層面去優化。
  3. 繼續從開發的角度說,雖然理論上開發應該做好適配,但是對於文字突然被放大兩倍,很多時候確實心有余而力不足。如果要做好,需要花費大量的時間和精力,並且需要設計和產品同學從設計上留出一些適配空間。
  4. 既然用戶選擇了用大字體來瀏覽頁面,他就應該知道這個頁面是被自己放大了,需要承擔頁面布局亂掉的結果。

 

一、用戶修改手機字體設置大小,影響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);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM