我是HullQin,公众号线下团聚游戏的作者(欢送关注公众号,发送加微信,交个敌人),转发本文前需取得作者HullQin受权。我独立开发了《联机桌游合集》,是个网页,能够很不便的跟敌人联机玩斗地主、五子棋等游戏,不免费没广告。还开发了《Dice Crush》加入Game Jam 2022。喜爱能够关注我 HullQin 噢~我有空了会分享做游戏的相干技术。
背景
之前的文章,我提到网页开发的一个常见问题:《在微信大字号模式下,网页款式乱了怎么办?》。上文中提供了一种解决方案,在用户调整微信字体大小后,能够保障网页字体大小不变,解决了款式错乱的问题。
然而下面的解决方案,背离了微信「关心模式」和「大字号模式」的初衷。所以我又写了一篇文章:《让你的网页,适配微信大字号模式!体验超好,快来珍藏》,介绍了几种计划,助你网页适配微信的大字号。
然而在iOS操作系统下(iPhone或iPad),依然有个问题。如果用户之前设置了网页的大字号,进入任意网页时,就有个显著的闪动:
然而在安卓,没有这个问题,会间接以大字号展现。
剖析起因
这与微信大字号的实现形式无关。而实现形式又与机型无关。包含以下3种:
安卓
在安卓,网页不会闪动,是因为安卓应用setTextZoom)设置字体比例。早在html加载前,就曾经设置好了,它也无需批改dom。
后续用户批改网页字号时,只须要安卓调用setTextZoom
即可(这是微信客户端实现的,咱们Web开发者无需关注)。
iPhone
iPhone闪动,是因为它必须等到html加载完,渲染页面的JS也执行完,再给body
标签增加text-size-adjust这个CSS款式(它须要批改dom)。
后续用户批改网页字号时,只需批改body
的text-size-adjust
这个CSS款式即可(这是微信客户端实现的,咱们Web开发者无需关注)。
解决思路
如果咱们能晓得用户设置的缩放比例,在咱们的JS被动给body
标签增加text-size-adjust)这个CSS款式(在渲染之前就增加),那么闪动的问题就解决了。
iPad
我明天钻研了一下,发现微信iPad大字号和iPhone大字号,实现形式竟然不一样!
iPad闪动,是因为它必须等到html加载完,渲染页面的JS也执行完,再给所有蕴含Text文本的标签增加style
属性,外面间接指定了计算后的font-size
。当然,网页元素的原始字号大小保留在了元素的属性mp-original-font-size
里。
动静赋值后,你的DOM构造会变成这样:
后续用户批改字体时,会依据mp-original-font-size
和放大比例,从新计算所有元素的font-size
,并动静设置所有元素的style
中的font-size
。
可能有敌人要问了:一次性设置这么多style
属性,这不会很卡吗?
没错,是很卡,在安卓、iPhone、iPad上设置大字号,就iPad最卡!
解决思路
如果咱们能晓得用户设置的缩放比例,在咱们的JS被动给所有的蕴含Text的元素增加font-size
这个CSS款式(在渲染之前就增加),那么闪动的问题就解决了。
开发代码,解决问题
我通过神秘形式,获取了一串神秘代码:JSON.parse(window.__wxWebEnv.getEnv()).fontScale
,在微信内置浏览器中,执行这个办法,是能够取得用户设置的缩放大小的。当然,你必须先援用「微信JS SDK」,能力用这个代码,否则会报错,倡议联合try catch应用它。
须要在html文件head引入:
<script src="//res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
上面针对iPhone和iPad这2种机型,别离给出解决方案。
iPhone
思路介绍:给body设置一个-webkit-text-size-adjust
款式,依照字号缩放比例设置即可。
const initIPhoneFontSize = () => { try { document.body.style.webkitTextSizeAdjust=JSON.parse(window.__wxWebEnv.getEnv()).fontScale+'%'; } catch {}};
iPad
思路介绍:
- 通过user agent判断浏览器类型和机型。如果发现是微信浏览器,且是iPad,那么就执行本段代码,调整初始字号。
- 如果以后用户字号
fontScale
为1,无需缩放,间接完结函数。 - 如果以后用户字号
fontScale
不是1,就先把整个body
暗藏,并持续以下步骤。 - 从
body
开始,递归遍历子节点,设置所有的mp-original-font-size
。 - 从
body
开始,递归遍历子节点,设置所有的style
里的font-size
,是依据mp-original-font-size
乘以fontScale
计算出来的。 - 全副赋值后,再把
body
展现进去。至此,初始字号设置结束,画面不再闪现。
const initIPadFontSize = () => { let fontScale = 1; try { fontScale = JSON.parse(window.__wxWebEnv.getEnv()).fontScale; } catch {} if (fontScale === 1) return; document.body.style.display='none'; const addOriginFontSize = (root) => { let flag = false; root.childNodes.forEach((node) => { if (node instanceof Text) { if (flag) return; flag = true; root.setAttribute('mp-original-font-size', getComputedStyle(root).fontSize); } else if (!(node instanceof HTMLScriptElement)) { addOriginFontSize(node); } }); }; const addFontSize = (nodes) => { nodes.forEach((node) => { const fontSize = node.getAttribute('mp-original-font-size'); if (fontSize) { node.style.fontSize = parseFloat(fontSize) * fontScale + 'px'; } if (node.children) addFontSize(Array.from(node.children)); }); }; addOriginFontSize(document.body); addFontSize(Array.from(document.body.children)); document.body.style.display='';};
判断机型,执行相应函数
if (/MicroMessenger/i.test(navigator.userAgent) { if (/iPad/i.test(navigator.userAgent)) initIPadFontSize(); else if (/iPhone/i.test(navigator.userAgent)) initIPhoneFontSize();}
补充阐明
如何实现:按需引入微信JS SDK?
如果用户不是应用的微信浏览器,那么齐全没有必要引入微信JS SDK。
你能够先判断user agent,再动静引入:
if (/MicroMessenger/i.test(navigator.userAgent) && /iPad|iPhone/i.test(navigator.userAgent)) { const scriptEl = document.createElement('script'); scriptEl.setAttribute('src', '//res.wx.qq.com/open/js/jweixin-1.6.0.js'); document.head.appendChild(scriptEl); scriptEl.addEventListener('load', () => { const fontScale = JSON.parse(window.__wxWebEnv.getEnv()).fontScale; });}
这样,在回调函数中,能够获取fontScale
。然而为了解决闪动问题,你可能须要在document.createElement('script')
前,先把body
暗藏(document.body.style.display='none'
)。
iPad的字号缩放形式有方法跟iPhone对立吗?
还真有。
只有咱们设置了这个变量:
window.__wxjs_ipadfontsolution = false;
那么在iPad中,缩放字号,也会通过-webkit-text-size-adjust
形式来缩放了。只是存在兼容性问题,一些iPad不反对这个款式,用户就无奈通过微信管制字号了。
此时,整个解决方案是最简洁的:
<script src="//res.wx.qq.com/open/js/jweixin-1.6.0.js"></script><!-- 下面的脚本,放在head里 --><!-- 上面的脚本,放在body里 --><script>try { if (/MicroMessenger/.test(navigator.userAgent) && /iPhone|iPad|iPod|iOS/i.test(navigator.userAgent)) { window.__wxjs_ipadfontsolution = false; document.body.style.webkitTextSizeAdjust=JSON.parse(window.__wxWebEnv.getEnv()).fontScale+'%'; }} catch {}</script>
如果你还心愿按需引入微信JS SDK,再做一些容错解决,整个逻辑就是这样子:
if (/MicroMessenger/i.test(navigator.userAgent) && /iPhone|iPad|iPod|iOS/i.test(navigator.userAgent)) { document.body.style.display = 'none'; const scriptEl = document.createElement('script'); scriptEl.setAttribute('src', '//res.wx.qq.com/open/js/jweixin-1.6.0.js'); document.head.appendChild(scriptEl); scriptEl.addEventListener('error', () => { document.body.style.display = ''; }); scriptEl.addEventListener('load', () => { try { window.__wxjs_ipadfontsolution = false; document.body.style.webkitTextSizeAdjust=JSON.parse(window.__wxWebEnv.getEnv()).fontScale+'%'; document.body.style.display = ''; } catch { document.body.style.display = ''; } });}
我就是用了这个形式,亲测无效。你能够点开我的网站 game.hullqin.cn 体验一下。
写在最初
我是HullQin,公众号线下团聚游戏的作者(欢送关注公众号,发送加微信,交个敌人),转发本文前需取得作者HullQin受权。我独立开发了《联机桌游合集》,是个网页,能够很不便的跟敌人联机玩斗地主、五子棋等游戏,不免费没广告。还开发了《Dice Crush》加入Game Jam 2022。喜爱能够关注我 HullQin 噢~我有空了会分享做游戏的相干技术。