安卓系统字体调整后前端rem适配方案问题探究

549 阅读3分钟

背景

部分使用了REM方式来实现自适应的前端页面,随安卓系统字体放大会出现素材和布局跟随变化问题

应用层解决方式 解决方案:

原先获取根字体方案:

const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize)
更改为如下实现:(根据实际1rem单位元素的宽度作为基准fontsize)

var h = document.getElementsByTagName('head')[0],
    d = document.createElement('div')
  d.style.width = '1rem'
  d.style.display = 'none'
  h.appendChild(d)
const rootFontSize = parseFloat(getComputedStyle(d, null).getPropertyValue('width'))

根本原因简述 为什么第二种没有问题呢?根据以往的知识1rem等于根节点的fontsize值 两段代码应该一个意思。为什么两个表现不一样了?调试了下真实webview效果如下图

image.png 调整字体大小后根节点fontsize1rem的值和自己创建一个元素宽度给1rem的值不一样!

顺着这个逻辑我们需要去探究两个知识点1、rem的定义2、安卓webview如何实现字体大小调整

一、rem的定义 我们看下w3c中rem的定义

rem unit Equal to the computed value of font-size on the root element. If used in the font-size property of the root element, or in a document with no root element, 1rem is equal to the initial value of the font-size property.

等于根元素上 font-size的计算值。 如果在根元素的font-size属性中使用,或者在没有根元素的文档中使用,则 1rem等于font-size属性的初始值。

二、安卓webview如何实现字体大小调整 安卓同事会借助 setTextZoom这个API实现webview字体大小变化. 调用setTextZoom发生了什么? 长话短说,我们从setTextZoom的java代码定位到chromium内核源码,发现了安卓webview特有的一段

void AwRenderFrameExt::SetTextZoomFactor(float zoom_factor) {
  // TODO(crbug.com/1085428): This will need to be set on every local root
  // when site isolation is used in android webview.
  DCHECK(render_frame()->IsMainFrame());

  blink::WebView* webview = GetWebView();
  if (!webview)
    return;

  // Hide selection and autofill popups.
  webview->CancelPagePopup();

  render_frame()->GetWebFrame()->FrameWidget()->SetTextZoomFactor(zoom_factor);
}

没有详细探究C代码 这边的变大逻辑看变量名应该是在渲染时执行的。 我们看到这个代码里面还有段注释标明这个地方还有bug // TODO(crbug.com/1085428): This will need to be set on every local root 有兴趣的可以看下 再结合源码中的一段注释

  // Sets the zoom factor for text only. Used in layout modes other than
  // Text Autosizing.
  SetTextZoomFactor(float zoom_factor);

结合如上信息我们推断在安卓webview中1rem最先的取值是根节点fontsize初始值。 之后由于安卓webview中setTextZoom特殊逻辑会对文本节点的fontsize做调整,表面上fontsize属性值还是1rem,其实已经经过再次计算。 而rem来做自适应我们是需要初始固定的的rem对应的值来进行一系列计算。并没有考虑到系统大小字体变化的倍数所以会有问题。 我们原先需要一个标准尺度,但是尺度在变化,所以出现问题。 也可以发现关于rem的标准定义在安卓webview中调用setTextZoom的情况下并没有遵循。