兼容安卓setTextZoom对h5布局影响

1,933 阅读3分钟

概述

本文讨论的是:安卓手机通过setTextZoom(int)设置字体缩放会如何影响h5页面布局,并给出一个兼容方案。

本文分为三个部分:

  1. 介绍setTextZoom(int)api
  2. 对h5页面具体的影响
  3. 如何兼容
  4. 具体实现

一、 setTextZoom(int)

setTextZoom(int textZoom)可以设置webview中加载页面字体变焦百分比,默认100。该设置通过观察发现会对css的font-size和line-height属性起缩放作用。

假设setTextZoom(150)(即为1.5倍放大),即使font-size通过内联样式设置为10px,通过getComputedStyle()获取,会发现最终会被渲染为15px,line-height也是如此,后面展示。

二、对h5页面的影响

首先,setTextZoom(int)会影响font-size、line-height值的最终渲染。

webview设置为setTextZoom(150)时,即使把一个元素的height和line-height都设置为相同的值,通过getComputedStyle观察最终渲染的px值会发现并不相同!会被1.5倍放大。

image.png

image.png

可见无论以什么单位设置line-height和font-size,最后都会影响到line-height和font-size,导致其被1.5倍放大,该属性与其他属性即使设为同一值但最终渲染的值并不一致从而影响页面样式布局。

image.png

三、兼容思路

当前很多h5页面都是通过给根节点设置一个font-size值,再配合rem实现移动端页面布局。但是setTextZoom(int)会影响最终的font-size、line-height值的渲染,导致页面展示不符合预期。

如果setTextZoom(150)放大了1.5倍,那么我们把根节点的font-size值相应缩小1.5倍是否可以解决问题?不可以,因为除了font-size、line-height外的其他值并不受setTextZoom的缩放影响,这两个属性正常了,其他属性就会异常了。

既然如此我们可以采用结合vw和rem的布局方式来解决问题,受影响的属性使用rem,然后再把根节点的font-size值相应缩小,不受影响的使用vw布局。

image.png

四、具体实现

结合vw和rem的布局方案可以采用,具体可以通过postcss-pxtorempostcss-px-to-viewport插件实现。

const postcssPxToViewport = require('postcss-px-to-viewport');
const postcssPxToRem = require('postcss-pxtorem');
module.exports = {
  // ...
  plugins: [
    // ...
    postcssPxToViewport({
      // options
      viewportWidth750// 设计稿宽度
      propList: ['*', '!font-size', '!line-height'] // 需要转换的css属性
      unitPrecision5// px转换后的小数保留位数,有时候不能整除
      minPixelValue1// 小于或等于`1px`时不转换为视窗单位

    }),
    postcssPxToRem({
      // options
      rootValue: 100,
      propList: ['font-size', 'line-height'], // 仅处理受setTextZoom影响的属性
    })
  ]
}

这样就可以结合vw和rem实现布局,那么如何知道页面是否受到了setTextZoom(int)影响,并获得对应的缩放倍数并处理呢?

const el = document.createElement('div')
el.style.lineHeight = `1rem`
el.style.height = `1rem`
el.style.display = 'none'
document.querySelectorAll('html')[0].appendChild(el)
// lineHeight 会被 WebView.getSettings().setTextZoom(**) 影响
const lineHeight = Number(window.getComputedStyle(el, null).lineHeight.replace('px', ''))
// height 不会被 WebView.getSettings().setTextZoom(**) 影响
const height = Number(window.getComputedStyle(el, null).height.replace('px', ''))
// 检查字体是否被缩放了
if (Math.abs(lineHeight - height) > 0.5) {
    // 按比例重置根节点的font-size, 1rem的heght值就是当前根节点的font-size值
    document.documentElement.style.fontSize = height * (height / lineHeight) + 'px'
}

以上就可以实现具体的兼容,那么有没有更简单的方法呢?

有!如果客户端能提供api 重置WebView.getSettings().setTextZoom(100) 那就直接什么都无需处理了。