乐趣区

关于前端:极致用户体验-微信设置大字号后iOS加载网页时闪动怎么办

我是 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)。

后续用户批改网页字号时,只需批改 bodytext-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

思路介绍:

  1. 通过 user agent 判断浏览器类型和机型。如果发现是微信浏览器,且是 iPad,那么就执行本段代码,调整初始字号。
  2. 如果以后用户字号 fontScale 为 1,无需缩放,间接完结函数。
  3. 如果以后用户字号 fontScale 不是 1,就先把整个 body 暗藏,并持续以下步骤。
  4. body 开始,递归遍历子节点,设置所有的mp-original-font-size
  5. body 开始,递归遍历子节点,设置所有的 style 里的 font-size,是依据mp-original-font-size 乘以 fontScale 计算出来的。
  6. 全副赋值后,再把 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 噢~我有空了会分享做游戏的相干技术。

退出移动版