主题
屏幕适配
前置知识
屏幕大小
屏幕大小 指的是屏幕对角线的长度,例如:5.0
寸,5.5
寸,6.0
寸...
屏幕分辨率
屏幕分辨率 指屏幕在横向、纵向上所拥有的物理像素点总数。一般表示用 n * m
表示。例如:iPhone 6 的屏幕为 750 * 1334
。
- 屏幕分辨率是一个固定值,屏幕生产出来就确定了,无法修改!!!
- 屏幕分辨率、显示分辨率是两个概念,系统设置中可以修改的是:显示分辨率。
- 显示分辨率是设备当前所用到的物理像素点数,也可以说:屏幕分辨率 >= 显示分辨率。
屏幕密度
屏幕密度(屏幕像素密度) 是指屏幕上每英寸里包含的物理像素点个数。有两种单位:
ppi(pixels per inch)
,主要用来衡量屏幕。dpi(dots per inch)
,用来衡量打印机等。
单位分类
单位可分为两类:
绝对单位 的大小是固定的,不受其他因素影响。
px
(像素):px
是最常见的绝对单位,表示屏幕上的一个物理像素。它的值在不同设备上通常是固定的,但在高分辨率屏幕上,一个 CSS 像素可能对应多个物理像素。pt
(点):pt
是一个印刷单位,1pt 等于 1/72 英寸。它通常用于打印样式表中。
相对单位 的大小是相对于其他元素的尺寸或视口的大小来确定的,适合用于响应式设计。
em
:em
是相对于当前元素的字体大小(font-size
)的单位。如果当前元素的字体大小是16px
,那么1em
就等于16px
。em
是继承的,因此它的大小会受到父元素字体大小的影响。rem
:rem
是相对于根元素(通常是<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
思路:
- 统一使用
rem
单位 - 使用媒体查询,根据不同尺寸,为根元素(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
思路:
在 js 中的核心逻辑:
jsdocument.documentElement.style.fontSize = docEl.clientWidth / 10 + 'px'
在 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
方案。
示例:
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
:
- postcss-pxtorem 是一款 PostCSS 插件,用于将 px 单位转化为 rem 单位
- postcss-px-to-viewport 是一款 PostCSS 插件,用于将 px 单位转化为 vw/vh 单位
大屏适配
常见方案
部分思路可参考移动端适配
- 百分比
- rem 单位 + 动态设置 html 的 font-size
- vw 单位
- scale 等比例缩放
- ...
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'
}