Skip to content

屏幕适配

前置知识

屏幕大小

屏幕大小 指的是屏幕对角线的长度,例如:5.0 寸,5.5 寸,6.0 寸...

屏幕分辨率

屏幕分辨率 指屏幕在横向、纵向上所拥有的物理像素点总数。一般表示用 n * m 表示。例如:iPhone 6 的屏幕为 750 * 1334

  • 屏幕分辨率是一个固定值,屏幕生产出来就确定了,无法修改!!!
  • 屏幕分辨率、显示分辨率是两个概念,系统设置中可以修改的是:显示分辨率。
  • 显示分辨率是设备当前所用到的物理像素点数,也可以说:屏幕分辨率 >= 显示分辨率

屏幕密度

屏幕密度(屏幕像素密度) 是指屏幕上每英寸里包含的物理像素点个数。有两种单位:

  • ppi(pixels per inch),主要用来衡量屏幕。
  • dpi(dots per inch),用来衡量打印机等。

单位分类

单位可分为两类:

  1. 绝对单位 的大小是固定的,不受其他因素影响。

    • px(像素):px 是最常见的绝对单位,表示屏幕上的一个物理像素。它的值在不同设备上通常是固定的,但在高分辨率屏幕上,一个 CSS 像素可能对应多个物理像素。

    • pt(点):pt 是一个印刷单位,1pt 等于 1/72 英寸。它通常用于打印样式表中。

  2. 相对单位 的大小是相对于其他元素的尺寸或视口的大小来确定的,适合用于响应式设计。

    • emem 是相对于当前元素的字体大小(font-size)的单位。如果当前元素的字体大小是 16px,那么 1em 就等于 16pxem 是继承的,因此它的大小会受到父元素字体大小的影响。

    • remrem 是相对于根元素(通常是 <html> 元素)的字体大小的单位。如果根元素的字体大小是 16px,那么 1rem 就等于 16px。与 em 不同,rem 不会受到父元素字体大小的影响,因此更加一致。

    • vw / vh

      • vw 表示视口宽度的 1%
      • vh 表示视口高度的 1%
      • 例如,50vw 表示视口宽度的 50%,100vh 表示视口高度的 100%。这些单位通常用于响应式设计中,以确保元素的大小能够根据视口的大小进行调整。
    • %(百分比):百分比单位是相对于父元素的宽度或高度的单位。例如,50% 表示父元素宽度或高度的 50%。

DPR

​DPR(Device Pixel Ratio,设备像素比):单一方向上设备【物理像素】和【设备独立像素】的比例。即:dpr = 物理像素 / 设备独立像素

可通过 window.devicePixelRatio 获取当前显示设备的物理像素分辨率与 CSS 像素分辨率之比。

设备的 ​DPR(Device Pixel Ratio,设备像素比)​ 是由硬件和操作系统决定的,通常不可更改。

设置视口

html
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

属性解析:

  • name="viewport"
    • 声明这是一个用于控制 移动端浏览器视口行为 的元标签。它是响应式设计的核心配置,直接影响网页在移动设备上的显示效果。
  • width=device-width
    • 将视口宽度设置为 设备的逻辑屏幕宽度(如 iPhone 的逻辑宽度为 375px 或 414px)。
    • 如果不设置,浏览器默认会用桌面端视口宽度(如 980px)渲染页面,导致移动端需要手动缩放才能正常查看内容。
  • initial-scale=1.0
    • 定义页面的 初始缩放比例为 1:1(即不缩放)。
    • 确保页面加载时以 100% 的比例显示,避免自动缩放导致的布局错乱。

补充说明:

  • maximum-scale=1.0 / minimum-scale=1.0:限制用户最大/最小缩放比例。设为 1.0 可完全禁用缩放(但需谨慎,可能影响可访问性)。
  • user-scalable=no:禁止用户手动缩放页面(不推荐!可能违反无障碍设计原则)。

移动端适配

rem + @media

思路

  1. 统一使用 rem 单位
  2. 使用媒体查询,根据不同尺寸,为根元素(html)设置不同的 font-size

示例

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      body {
        margin: 0;
      }

      @media only screen and (device-width: 375px) {
        html {
          font-size: 50px;
        }
      }

      @media only screen and (device-width: 750px) {
        html {
          font-size: 100px; /* 基准值 */
        }
      }

      @media only screen and (device-width: 1920px) {
        html {
          font-size: 256px;
        }
      }

      .text {
        font-size: 0.75rem;
      }

      .box {
        width: 3.5rem;
        height: 3.5rem;
        background-color: pink;
      }
    </style>
  </head>
  <body>
    <div class="text">一二三四五六七八九十</div>
    <div class="box">1/2</div>
  </body>
</html>
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      body {
        margin: 0;
      }

      @media only screen and (device-width: 375px) {
        html {
          font-size: 37.5px;
        }
      }

      @media only screen and (device-width: 750px) {
        html {
          font-size: 75px;
        }
      }

      @media only screen and (device-width: 1920px) {
        html {
          font-size: 192px;
        }
      }

      .text {
        font-size: 1rem; /* 基准值 */
      }

      .box {
        width: 5rem;
        height: 5rem;
        background-color: pink;
      }
    </style>
  </head>
  <body>
    <div class="text">一二三四五六七八九十</div>
    <div class="box">1/2</div>
  </body>
</html>

不推荐

  • 不太灵活且适配繁琐(移动端尺寸众多,需要为每一台设备都兼容一次)

rem + flexible

思路:

  1. 在 js 中的核心逻辑:

    js
    document.documentElement.style.fontSize = docEl.clientWidth / 10 + 'px'
  2. 在 css 中,再根据设计图,计算出元素的 rem 大小。

示例

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="./flexible.js"></script>
    <style>
      body {
        margin: 0;
      }
      .text {
        font-size: 1rem;
      }
      .box {
        width: 5rem;
        height: 5rem;
        background-color: pink;
      }

      .demo {
        /* 256 * 128 */
        width: calc(256 / 75 * 1rem);
        height: calc(128 / 75 * 1rem);
        background-color: #aaa;
        font-size: calc(24 / 75 * 1rem);
      }
    </style>
  </head>
  <body>
    <div class="text">一二三四五六七八九十</div>
    <div class="box">1/2</div>
    <div class="demo">
      <div>wh -> 256 * 128</div>
      <div>fs -> 24px</div>
    </div>
  </body>
</html>
js
// https://github.com/amfe/lib-flexible
;(function flexible(window, document) {
  var docEl = document.documentElement
  var dpr = window.devicePixelRatio || 1

  // adjust body font size
  function setBodyFontSize() {
    if (document.body) {
      document.body.style.fontSize = 12 * dpr + 'px'
    } else {
      document.addEventListener('DOMContentLoaded', setBodyFontSize)
    }
  }
  setBodyFontSize()

  // set 1rem = viewWidth / 10
  function setRemUnit() {
    var rem = docEl.clientWidth / 10
    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()
    }
  })

  // detect 0.5px supports
  if (dpr >= 2) {
    var fakeBody = document.createElement('body')
    var testElement = document.createElement('div')
    testElement.style.border = '.5px solid transparent'
    fakeBody.appendChild(testElement)
    docEl.appendChild(fakeBody)
    if (testElement.offsetHeight === 1) {
      docEl.classList.add('hairlines')
    }
    docEl.removeChild(fakeBody)
  }
})(window, document)

vw/vh

由于 viewport 单位 得到众多浏览器的兼容,lib-flexible 这个过渡方案已经可以放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用 viewport 来替代此方案。

综上,同样的思路,vw/vh 方案可替代 rem + flexible 方案。

注意

该方案仍然存在 “单位换算” 的问题!

如果需要,可安装 px to rem & rpx & vw (cssrem) 插件辅助开发。

示例

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      body {
        margin: 0;
      }
      .text {
        font-size: 10vw;
      }
      .box {
        width: 50vw;
        height: 50vw;
        background-color: pink;
      }

      .demo {
        /* 256 * 128 */
        width: calc(256 / 75 * 10vw);
        height: calc(128 / 75 * 10vw);
        background-color: #aaa;
        font-size: calc(24 / 75 * 10vw);
      }
    </style>
  </head>
  <body>
    <div class="text">一二三四五六七八九十</div>
    <div class="box">1/2</div>
    <div class="demo">
      <div>wh -> 256 * 128</div>
      <div>fs -> 24px</div>
    </div>
  </body>
</html>

px 自动转换

当然啦,如果配合构建工具开发可太方便了!稍微配置下,即可 px 无缝转 rem 或者 vw/vh

大屏适配

常见方案

部分思路可参考移动端适配

  1. 百分比
  2. rem 单位 + 动态设置 html 的 font-size
  3. vw 单位
  4. scale 等比例缩放
  5. ...

scale

当尺寸不兼容时,会存在“空白区域”。

vue
<script setup lang="ts">
const screenRef = ref()

const designWidth = 3840 // 设计稿宽度
const designHeight = 2160 // 设计稿高度

const getScale = () => {
  const ww = window.innerWidth / designWidth
  const wh = window.innerHeight / designHeight
  return ww < wh ? ww : wh
}

const updateScale = () => {
  screenRef.value.style.transform = `scale(${getScale()}) translate(-50%, -50%)`
}

window.addEventListener('resize', updateScale)

onMounted(() => {
  updateScale()
})

onUnmounted(() => {
  window.removeEventListener('resize', updateScale)
})
</script>

<template>
  <div class="scale-container">
    <div class="screen" ref="screenRef" :style="{ width: `${designWidth}px`, height: `${designHeight}px` }">
      <div class="box">
        <div>测试文字</div>
        <ExampleChart />
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.scale-container {
  position: relative;
  width: 100vw;
  height: 100vh;
  background: url(/bg/a1.jpg) 0 0 / cover no-repeat;
  font-size: 32px;
  color: #fff;

  .screen {
    position: fixed;
    top: 50%;
    left: 50%;
    background-color: rgba(0, 0, 0, 0.3);
    transform-origin: top left;

    .box {
      width: 1200px;
      height: 800px;
      background-color: rgba(255, 255, 255, 0.3);
    }
  }
}
</style>

vw/vh

vue
<script setup lang="ts">
const screenRef = ref()
</script>

<template>
  <div class="scale-container">
    <div class="screen" ref="screenRef">
      <div class="box">
        <p>测试文字</p>
        <ExampleChart />
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
@use './utils.scss' as *;

.scale-container {
  width: 100vw;
  height: 100vh;
  background: url(/bg/a1.jpg) center / cover no-repeat;
  font-size: px2vw(32);

  .screen {
    width: px2vw(3840);
    height: px2vh(2160);
    background-color: transparent;

    .box {
      width: px2vw(1200);
      height: px2vh(800);
    }
  }
}
</style>
scss
@use 'sass:math';

$desginWidth: 3840; // 设计稿宽度
$designHeight: 2160; // 设计稿高度

@function px2vw($px) {
  @return math.div($px, $desginWidth) * 100vw;
}

@function px2vh($px) {
  @return math.div($px, $designHeight) * 100vh;
}
ts
const designWidth = 3840 // 设计稿宽度
const designHeight = 2160 // 设计稿高度

export function px2vw(px: number) {
  return (px / designWidth) * 100 + 'vw'
}

export function px2vh(px: number) {
  return (px / designHeight) * 100 + 'vh'
}

基于 MIT 许可发布