本文同步發佈於富途web開發團隊博客 futu.im/article/mob…javascript
不少webview提供了調整頁面字體大小的功能,例如手機QQ、微信、部分Android內置瀏覽器等。大部分瀏覽器調整字體只會致使字體顯示大小發生改變,其餘元素的大小不受影響。但對於結構稍微複雜一點的頁面,字體大小的變更就足以致使頁面佈局亂掉,致使文本不居中、文字折行、佈局混亂等問題。php
24-mobile-browser-font-size/1.png)做爲前端工程師,碰到頁面亂掉的狀況就會以爲很無辜了,明明是你本身放大的字體,放大了卻還要我來承擔排版亂掉的後果,多委屈啊。好久之前,在PC端,好歹咱們還能夠提示用戶按CTLR + 0將頁面的比例調整回來,如今在移動端,卻很難阻止用戶縮放字體大小。css
然而即便再委屈,當問題來了以後仍是須要處理的,誰讓咱們是前端工程師呢?(笑)html
因爲並非很清楚各個平臺(瀏覽器)放大字體的機制,我分別諮詢了咱們 iOS 和 Android 的同事,得知在調整字體大小時時,2個客戶端的處理方式不一樣。前端
iOS上須要調整 webview 的字體大小時,是經過給 body 設置 -webkit-text-size-adjust
屬性實現的:java
既然這樣,咱們應該能夠經過JS取到這個屬性:android
var body = document.body;
alert(body.getAttribute('style'));
複製代碼
圖上能夠看到,當頁面文字被放大時,確實多了一個-webkit-text-size-adjust
屬性。web
Android經過給 webview 設置字體的縮放來完成,具體的API是setTextZoom(int)
。瀏覽器
咱們經過一個demo頁面來查看效果:bash
從圖中能夠看到,文字確實是被放大了。例如「文字大小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;
複製代碼
可是這樣作仍然有幾個問題:
這幾個問題並不如想象中的好解決。因而另闢蹊徑,看看是否有一勞永逸的辦法。腦海中很快冒出一個名詞——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微信中,也能夠藉助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 });
});
}
})();
複製代碼
做爲用戶量龐大的APP之一,QQ也提供了禁止調整字體大小的方案,android qq中能夠自定義webview顯示的控件,經過在url中加入指定參數便可。見如何定製手Q的Webview.
理論上,www.futu5.com/?_wv=128訪問這個連接,功能菜單中不會出現調整字體大小的按鈕。可是,可是,可是,在我實測過程當中,全部的參數中,就只有【128隱藏字體項不生效】。不知道是QQ的bug仍是有意爲之,目前已提交反饋,但未收到迴應。
在組內分享的時候,你們對於字體大小調整這個頭疼的問題各自有不一樣的見解,大概有怎麼幾種聲音:
做爲開發者,我心裏是傾向於第4種聲音的,可是從產品的角度考慮,這個鍋不能丟給用戶。另外一方面,在有限時間中,又只能從大部分人的都以爲ok的視覺體驗爲標準來展開開發,時間充足的狀況纔有可能再對大字體另作適配方面的考慮(這可能嗎?)。
這也許是產品、設計、開發、老闆都比較能接受的結果,能夠類比國內大部分網站在無障礙瀏覽上的工做量,不完美,但又很無奈。
TooBug對本文進行了審校。