css/html/优化/前端

H5页面适配及微信默认字号问题的最佳实践

csxiaoyao · 4月4日 · 2021年 · 本文5664字 · 阅读15分钟61

H5页面适配及微信默认字号问题的最佳实践

Write By CS逍遥剑仙
我的主页: csxiaoyao.com
GitHub: github.com/csxiaoyaojianxian
Email: sunjianfeng@csxiaoyao.com
QQ: 1724338257

1. css 中的单位

1.1 基于像素 px

最常用的 绝对单位,按精确像素计算

1.2 基于字号 em / rem

  • em相对单位,基准为父节点字体大小,若自身定义了 font-size 则按自身计算,不推荐使用

  • rem相对单位,css3新加,按照根节点 <html> 的字号作为基准,下方提供的设置根节点 62.5% 的方案并不推荐,具体实践见第二节的介绍

/* 根节点字体大小设置为 62.5%,即 10px 可以方便计算,否则将以浏览器默认字号 16px 为基准 */
/* 10 ÷ 16 × 100% = 62.5% */
/* 但 chrome 最小的字体大小为 12px,因此浏览器中的字体均 >= 1.2rem */
html { font-size: 62.5%; }
body { font-size: 1.4rem; } /* 1.4 × 10px = 14px */

1.3 基于视窗 vw / vh / vmin / vmax

  • vw / vh 即 viewpoint width / height,按照 视窗 的宽高的百分比进行计算,和 css 中的 % 按照父元素的宽高作为计算基准的方式不同

  • vmin / vmax 取视窗宽高二者中较小 / 大值的百分比进行计算

/* 元素始终在屏幕上可见 */
.box {
    width: 100vmin;
    height: 100vmin;
}
/* 元素始终铺满整屏 */
.box {
    width: 100vmax;
    height: 100vmax;
}

1.4 其他 % / vm / pc / pt / ch / ex …

  • % 百分比,一般相对于父元素,但对于 position: absolute; 的元素是相对于已定位的父元素,对于 position: fixed; 的元素是相对于可视窗口,并且当父元素没有指定高度时,子元素设置百分比没有效果,高度直接为子元素的实际高度

  • vm css3 新单位,相对于视窗宽高较小的那个的百分比,兼容性较差

下面的单位几乎用不到:

  • in

  • cm 厘米

  • mm 毫米

  • pt point,约1/72寸

  • pc pica,大约6pt,1/6寸

  • ch

  • ex

2. 使用 rem 进行移动端页面适配

移动端适配最简单的是通过js动态计算 viewport 的缩放值,但过于粗暴,会导致页面图片文字失真模糊。目前,移动端页面一般使用 remvw / vh 开发会较为方便,下面以 rem 为例:

为了方便计算,约定:100px = 1rem,若设计师给到一张宽度为 750px 的设计稿,那么可以设置 html 的字体大小为 设备宽度 / 设计稿宽度 * 100 个px像素,以 iPhone 6/7/8 的宽度 375px 为例,则 html 字体的大小为 50px,即在宽度为 375 px 的设备上,1rem = 50px。对于开发人员,一个宽度为 50px 的 div,就可以很轻松的通过除以 100,计算出对应的 rem 为 0.5rem,不需要再根据各种机型进行适配,0.5rem 换算到 iPhone 6/7/8 为 25px,而放到 750px 宽的设备上对应的是 50px。

总体来说,使用 rem 进行适配需要以下四步:

  • 先根据设计稿尺寸使用 px 完成页面

  • 设置 meta,控制视口宽度,按 1:1 比例渲染页面

<meta name="viewport" content="initial-scale=1,maximum-scale=1,minimum-scale=1">
  • 动态设置 html 的 font-size
var clientWidth = document.documentElement.clientWidth
document.documentElement.style.fontSize = 100 * clientWidth / 750 + 'px'

建议设置监听,在页面尺寸变化时仍能正常展示

if (!document.addEventListener || !window.addEventListener) return
document.addEventListener('DOMContentLoaded', setScale, false)
window.addEventListener('orientationchange' in win ? 'orientationchange' : 'resize', setScale, false)
window.addEventListener('load', setScale, false)
  • 将页面各元素的 px 值除以 100 转换为 rem

3. 横版页面的 rem 适配

上面第二部分通过动态设置 html 的 font-size 已经实现了页面随设计稿比例缩放,这种方式是页面宽度 100% 撑满设备宽度的,但是很多情况下,我们更希望部分横版页面能够高度撑满设备高度,而左右部分留白,此时有两种方式可以实现:

  • html 的 font-size 依据页面高度进行设置,假设横版设计稿高度为 375px
var clientHeight = document.documentElement.clientHeight
document.documentElement.style.fontSize = 100 * clientHeight / 375 + 'px'
  • 限制横版页面的最大宽度,假设最大宽度为 670px 时,左右留白适配合适
var maxWidth = 670
var calWidth = Math.min(document.documentElement.clientWidth, maxWidth)
document.documentElement.style.fontSize = 100 * calWidth / 750 + 'px'

4. 解决微信等第三方App的默认字号问题

微信等 App 内可以设置默认字号,若用户修改了默认文字大小,会给上述的适配造成困扰,解决方法是先获取 App 的原始缩放比例,再在此基础上计算 font-size,实现如下:

// 创建1rem宽度的不可见元素,用于计算原始缩放比例
var scaleDom = (function () {
  var scaleDom = document.createElement('div')
  scaleDom.style.cssText = 'width:1rem;height:0;overflow:hidden;position:absolute;z-index:-2;visibility:hidden;'
  document.body.appendChild(scaleDom)
  return scaleDom
})()

// 计算使用过fontSize缩放(如微信)下的原始缩放比例
function getOriginScale () {
  var htmlFontSize = Number(String(document.querySelector('html').style.fontSize || 16).replace('px', ''))
  var instanceWidth = Number(String(window.getComputedStyle ? window.getComputedStyle(scaleDom).width : scaleDom.offsetWidth).replace('px', ''))
  var scale = (htmlFontSize && instanceWidth) ? htmlFontSize / instanceWidth : 1
  return scale
}

// 设置 html 用于处理 rem 的 font-size
function setScale () {
  var scale = getOriginScale()
  var clientWidth = document.documentElement.clientWidth
  if (!clientWidth) return
  window.uimakerScale = clientWidth / designWidth * scale
  document.documentElement.style.fontSize = 100 * window.uimakerScale + 'px'
}

5. 最佳实践

针对上述的适配方案,本文提供一套已在 html 自助化系统的生产环境中使用的适配代码作为最佳实践。

首先是 html 代码中需要配置视窗参数:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  ...
</head>
<body data-page-type="{{jsonData.pageType}}" data-design-width="{{jsonData.width}}" data-max-width="{{jsonData.maxWidth}}">
  ...
</body>
</html>

在初始化代码中执行下面的自执行函数,即可完成适配。

;(function (doc, win) {
  // 页面数据
  var pageType = parseInt(doc.body.getAttribute('data-page-type')) || 0 // 页面方向 0竖1横
  var designWidth = parseInt(doc.body.getAttribute('data-design-width')) || 750 // 设计宽度
  var maxWidth = parseInt(doc.body.getAttribute('data-max-width')) || designWidth // 最大页面适配宽度

  // 创建1rem宽度的不可见元素,用于计算原始缩放比例
  var scaleDom = (function () {
    var scaleDom = doc.createElement('div')
    scaleDom.style.cssText = 'width:1rem;height:0;overflow:hidden;position:absolute;z-index:-2;visibility:hidden;'
    doc.body.appendChild(scaleDom)
    return scaleDom
  })()

  // 计算使用过fontSize缩放(如微信)下的原始缩放比例
  function getOriginScale () {
    var htmlFontSize = Number(String(doc.querySelector('html').style.fontSize || 16).replace('px', ''))
    var instanceWidth = Number(String(win.getComputedStyle ? win.getComputedStyle(scaleDom).width : scaleDom.offsetWidth).replace('px', ''))
    var scale = (htmlFontSize && instanceWidth) ? htmlFontSize / instanceWidth : 1
    return scale
  }

  // 设置 html 用于处理 rem 的 font-size 和 页面二次缩放
  function setScale () {
    var scale = getOriginScale()
    var clientWidth = doc.documentElement.clientWidth
    var clientHeight = doc.documentElement.clientHeight
    if (!clientWidth || !clientHeight) return
    var calWidth = maxWidth > 0 ? Math.min(clientWidth, maxWidth) : clientWidth
    win.uimakerScale = calWidth / designWidth * scale
    doc.documentElement.style.fontSize = 100 * win.uimakerScale + 'px'
  }

  setScale()
  setTimeout(() => { setScale() }, 300)

  if (!doc.addEventListener || !win.addEventListener) return
  doc.addEventListener('DOMContentLoaded', setScale, false)
  win.addEventListener('orientationchange' in win ? 'orientationchange' : 'resize', setScale, false)
  win.addEventListener('load', setScale, false)
})(document, window)

6. 总结

移动端的适配已是老生常谈,市面上也有不少成熟的第三方库,但或多或少需要进行额外的配置,本文的方案是结合本人在一个自助化生成网页的项目的生产环境的实践中总结得出,若有更好的方案和建议欢迎和我交流。

H5页面适配及微信默认字号问题的最佳实践-禅林阆苑

0 条回应

×