本文同步发布于富途web开发团队博客 futu.im/article/mob…javascript
不少webview提供了调整页面字体大小的功能,例如手机QQ、微信、部分Android内置浏览器等。大部分浏览器调整字体只会致使字体显示大小发生改变,其余元素的大小不受影响。但对于结构稍微复杂一点的页面,字体大小的变更就足以致使页面布局乱掉,致使文本不居中、文字折行、布局混乱等问题。php
做为前端工程师,碰到页面乱掉的状况就会以为很无辜了,明明是你本身放大的字体,放大了却还要我来承担排版乱掉的后果,多委屈啊。好久之前,在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对本文进行了审校。