1. 程式人生 > >移動端用戶設置字體放大導致的問題

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

ofo 技術 dom元素 獲取 方式 else 註意 很快 客戶

問題背景

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

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

技術分享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文檔

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

聲音和思考

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

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

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