CSS移动端屏幕适配

8/31/2023 CSS

在社会的发展中,移动设备已经是我们日常生活不可或缺的一部分,那么对于 H5 开发者来说,如何兼容 css 移动端屏幕也是考核的关键。

接下来我会通过以下几点来切入,让读者对于移动端适配有更深入的了解

  1. 视口(viewport)
  2. Meta 标签
  3. 解决方案
  4. 方案对比与总结

# 视口(viewport)

在 Web 开发中,视口(Viewport)是指浏览器窗口中可见内容的部分,视口可以分为以下几种类型:

  1. 布局视口(Layout Viewport):这是网页布局的基础,它指的是文档在浏览器窗口中的大小。初始情况下,布局视口的宽度通常等于设备的屏幕宽度,而高度会根据内容的多少自动调整。在桌面浏览器上,用户可以通过调整浏览器窗口大小来改变布局视口的尺寸。
  2. 视觉视口(Visual Viewport):这是用户在浏览器中实际看到的内容区域。用户可以通过滚动网页来改变视觉视口的位置,从而浏览页面的不同部分。
  3. 理想视口(Ideal Viewport):为了在移动设备上获得更好的用户体验,可以通过在 HTML 文档中设置 <meta> 标签来指定理想视口的大小。这样做可以确保网页在移动设备上按照预期的方式显示,而不是被放大或缩小。
  4. 设备像素比(Device Pixel Ratio):设备像素比是设备物理像素与设备独立像素(CSS 像素)之间的比率。在高分辨率屏幕上,一个 CSS 像素可能对应多个物理像素。了解设备像素比很重要,因为它可以帮助开发者优化网页在不同屏幕上的显示效果。

在 PC 端中布局视口和视觉视口是一样的,不需要作任何区分。

但在移动设备上,还可能受到设备屏幕的物理尺寸影响以及像素比影响,例如 iphone 6 的布局视口是 750px, 像素比(dpr)是 2, 那么它的视觉视口就是 375px。

移动端会把 750px 的内容的缩小放到布局视口让用户浏览。

在这种情况下去开发样式,如果设置宽度位50px, 那么在所有机型都是50px。但在大屏幕移动设备下会显示太小,在小屏幕设备上就会显示太大。

并且在日常开发中,设计部的同事只会出一个宽度的设计稿,所以所有元素的单位都会以该设计稿为标准去开发。

Note:一般的设计稿是375px,就是以 iphone6 的屏幕宽去设计,所以下面的方法都是以375px去作示例。

# Meta 标签

在介绍解决方案之间,需要简单介绍一下meta viewport这个属性。因为接下来的方案中都必须设置这个 viewport 属性。

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
含义 默认值 可设置值
width 控制视口的宽度 可设置为特定像素数(如'width=600'),也可以设置为特殊值 device-width,即  https://developer.mozilla.org/zh-CN/docs/Web/CSS/length#%E8%A7%86%E5%8F%A3_viewport_%E6%AF%94%E4%BE%8B%E7%9A%84%E9%95%BF%E5%BA%A6,100% 的视口宽度。最小值为  1。最大值为  10000。负值会被忽略。
height 控制视口的高度 可设置为特定像素数(如  width=600),也可以设置为特殊值  device-height,即  https://developer.mozilla.org/zh-CN/docs/Web/CSS/length#%E8%A7%86%E5%8F%A3_viewport_%E6%AF%94%E4%BE%8B%E7%9A%84%E9%95%BF%E5%BA%A6,100% 的视口高度。最小值为  1。最大值为  10000。负值会被忽略。
initial-scale 控制页面首次加载时显示的缩放倍数 1 最小值是  0.1。最大值是  10。默认值为  1。负值会被忽略。
minimum-scale 控制页面允许缩小的倍数 1 最小值是  0.1。最大值是  10。默认值为  1。负值会被忽略。
maximum-scale 控制页面允许放大的倍数 1 设置一个低于  3  的值将不具备无障碍访问性。最小值是  0.1。最大值是  10。默认值为  1。负值会被忽略。
user-scalable 控制是否允许页面上的放大和缩小操作 1 或者 yes 有效值为  0、1、yes  或  no。默认值为  1,与  yes  相同。将值设置为  0(即与  no  相同)将违反 Web 内容无障碍指南(WCAG)。
interactive-widget 指定交互式 UI 组件(如虚拟键盘)对页面视口的影响 resizes-visual 有效值:resizes-visual、resizes-content  或  overlays-content。

一般来说,用上方示例的值来确保移动网页在不同设备上以最佳方式呈现。结合上方的表格,示例的意思是 “确保移动设备上的网页以实际设备宽度进行显示,不允许用户进行缩放操作”。

# 解决方案

为了实现网页在不同设备上呈现,最终要实现的效果是,我设置一个正方形是 100px * 100px360px宽度的手机上需要显示 98px左右的正方形,在414px宽度的手机上显示 103px左右的正方形,这样网页就跟设计部的原稿就一样了。

为了实现上面所说的效果,可以参考下面这些方案:

  1. viewport 值设置
  2. rem + html font-size 方案
  3. @media + vw + rem 的方案

# 1. viewport 值方案

<script type="text/javascript">
	var baseWidth = 375;
  var scale = 1;
  if (/Android (\d+(\.\d+)?)/.test(navigator.userAgent)) {
    var version = parseFloat(RegExp.$1);
    if (version > 2.3) {
      var phoneScale = parseInt(window.innerWidth || document.documentElement.clientWidth || 375) * scale / baseWidth;
      document.write('<meta name="viewport" content="width=' + baseWidth + ', minimum-scale = ' + phoneScale + ', maximum-scale = ' + phoneScale + ', target-densitydpi=device-dpi">');
    } else {
      document.write('<meta name="viewport" content="width=' + baseWidth + ', target-densitydpi=device-dpi">');
    }
  } else {
    document.write('<meta name="viewport" content="width=' + baseWidth + ', user-scalable=no, target-densitydpi=device-dpi">');
  }
</script>

上方的代码是根据设备的视图宽度和预定义的比例来动态生成适当的视口设置,以确保网页在不同设备上以一致的缩放级别呈现,从而适应不同屏幕大小和分辨率。

以 375px 的设计稿为例,然后在不同设备上进行缩放来处理。

这种方案特别适合在通用架构上去应用,例如一些公司级别的落地页,只要统一了设计稿输出标准,就能给各个业务线复用。

# 2. rem + html font-size 方案

rem单位是相对于 html 元素的font-size去计算大小的,例如 html 的font-size是 37.5px,那么1rem就是37.5px,那么根据这个特性来说,我们可以通过设置 html 的font-size,实现整体元素的放大缩小。

这种方案是目前业界最多人使用的。

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<script type="text/javascript">
(function flexible (window, document) {
  var docEl = document.documentElement
  var dpr = window.devicePixelRatio || 1
	var baseWidth = 375; // 设计稿的宽度
	function setBodyFontSize () {
    if (document.body) {
      document.body.style.fontSize = (12 * dpr) + 'px'
    }
    else {
      document.addEventListener('DOMContentLoaded', setBodyFontSize)
    }
  }
  setBodyFontSize();

  function setRemUnit () {
    var rem = (docEl.clientWidth / baseWidth) * 100;
    docEl.style.fontSize = rem + 'px'
  }

  setRemUnit()

  // reset rem unit on page resize
  window.addEventListener('resize', setRemUnit)
  window.addEventListener('pageshow', function (e) {
    if (e.persisted) {
      setRemUnit()
    }
  })
}(window, document))
</script>

因为使用了rem,所以开发者在开发的时候就需要通过换算,将设计稿上的宽度 / 100去换成 rem 单位。例如375px就是3.75rem

如果配合构建工具postcss,就可以自动换算。

安装插件postcss-pxtorem,在 loader 中的 options 加上下面的处理,在代码中就可以直接按照设计稿的 px 单位去开发,无需再手动计算。

postcssOptions: {
  plugins: [
    [
      'postcss-pxtorem',
      {
        rootValue: 100,
        propList: ['*'],
      },
    ],
  ],
},

# 3. @media + vw + rem 的方案

vw 是按照视觉视口单位去计算的,100vw 就是等于视口宽度。例如视口宽度是 375,1vw 就是 3.75px。

那么按照这种方式,上面的 Javascript 代码可以完全废弃掉。使用下面的方式。

<style>
:root {
	font-size: 10vw;
}

@media screen and (min-width: 1025px) { // 可以是适配PC端,但也要根据设计稿去调整
	font-size: 24px;
}
</style>

将 loader 的 rootValue 单位修改成 37.5,也能实现上方 Javascript 所实现的效果

postcssOptions: {
  plugins: [
    [
      'postcss-pxtorem',
      {
        rootValue: 37.5,
        propList: ['*'],
      },
    ],
  ],
},

# 方案对比与总结

以上三种方案都是实现响应式布局,看业务的需求。

优点 缺点
viewport 值方案 符合基础架构使用,不需要使用 webpack 等构建工具 不灵活,不能响应式处理横屏这种情况。
rem + html font-size 方案 基本上兼容主流场景,业界常用。 通过 Javascript 计算,性能有一定影响
@media + vw + rem 的方案 无需 Javascript 去计算。 可能不兼容旧版移动端,适合新业务使用。