前端多端响应式适配方案

什么是响应式布局?

响应式布局指的是同一页面在不同屏幕尺寸下展示不同的页面布局。从这句话可以看出我们如果想要达到这样的目的,那么就必须要能够检测/感知页面屏幕尺寸的变化,在不同的尺寸下修改对应的CSS,以达到响应式的效果。

我们通常有两种方式检测屏幕尺寸的变化:

  1. 通过JS,利用resize事件来监听网页尺寸的变化,然后通过document来获取页面宽度 // 监听屏幕尺寸变化 window.addEventListener("resize", (event) => { console.log("当前屏幕宽度", document.documentElement.clientWidth) })

  2. 第二种是通过CSS来实现,通过@media媒体查询绑定该设备下的CSS样式,当且仅当该媒体查询与正在使用其内容的设备匹配时,该CSS块才能应用于该文档。例如: div.container { background-color: skyblue } // 设备宽度大于640px @media screen and (min-width: 640px) { div.container { background-color: blue } } 上面这段代码给了div一个默认背景色为天蓝色,默认应用在宽度小于640px设备中。然后当设备宽达到或者超过640px时更改背景颜色为蓝色。这种先从移动端开始布局的适配方法被称为是移动优先方案。

移动端优先已经成为一种流行的设计方法,特别是随着移动设备的普及和移动互联网的快速发展。然而,选择哪种方法应该基于项目具体情况。在某些情况下,桌面端优先可能更合适。重要的是要确保你的网页在所有设备上都能提供良好的用户体验

移动端优先

对于移动设备而言,不同的设备分辨率上会有较大的差异。下面是同样一段代码,在IPhone 6和IPhone 6 Plus上的实现效果图:

(IPhone 6)

(IPhone 6 Plus)

可以看到最大规格这一行实现效果不同,这是因为两个设备宽不同,当我们为文字指定固定大小的时候,可能在大屏上正好显示,小屏就会超出,和我们想要达到的效果有所出入。想要解决这个问题,我们主要有两种方案:

  1. rem
  2. viewport

rem适配

rem(root em)单位是一种相对单位,它相对于 HTML 元素的字体大小来计算。如果我们使用rem作为文字大小单位,同时html根标签fontSize可以根据屏幕的大小自动发生变化,那么就可以在大屏幕上显示大文字,小屏幕上显示小文字了。例如:

xml 复制代码
<style>
    div {
        font-size: 0.4rem
    }
</style>
// ....
<script>
    const MAX_FONT_SIZE = 42;
    document.addEventListener('DOMContentLoaded', () => {
        const html = document.querySelector('html')
        let fontSize = window.innerWidth / 10 * window.devicePixelRatio
        
        fontSize = fontSize > MAX_FONT_SIZE ? MAX_FONT_SIZE : fontSize
        
        html.style.fontSize = fontSize + 'px'
    })
</script>

通过这种方案元素/布局可以实时响应窗口大小的变化,字体大小可以根据屏幕宽度连续变化,提供更平滑的响应式体验

如果我们对 设备宽度适配 有明确的预设目标,且希望通过 CSS 来优化不同屏幕尺寸下的字体显示,也可以通过下面这种方式(不过这种方式灵活性没有上面好,对于某个区间的例如处于75em和56.25em之间,即使屏幕发生变化了,字体大小也只是按照区间固定的比例变化,无法进行细粒度的动态调整,适合一些简单的响应式设计):

css 复制代码
html {
  font-size: 62.5%;
}
@media only screen and (max-width: 75em) {
  html {
    font-size: 56.25%;
  }
}
@media only screen and (max-width: 56.25em) {
  html {
    font-size: 50%;
  }
}
@media only screen and (min-width: 112.5em) {
  html {
    font-size: 75%;
  }
}

浏览器默认的字体大小通常是16px,这是不变的,我们一开始基于浏览器的默认值16px来计算字体大小16px * 62.5% = 10pxrem(root em)单位是一种相对单位,它相对于 HTML 元素的字体大小来计算,所以在后续中1rem就等于10px。但是无论如何设置html {font-size},浏览器的默认值是始终存在的,并不会因为项目的设置而变化,这个默认值会被用于:

  • 媒体查询中 em 的计算基准。

  • 当没有定义 html { font-size } 时,remem 的初始基准

所以即使我们在第四行媒体查询中编写了max-width: 75em,也不是根据10px计算,而是根据16px默认值去计算的。有人会问那项目中的em也是根据浏览器默认的16px计算,那还谈什么响应变动啊,根本就固定了啊。并不是这样,项目中 CSS 的 em 基于其父元素的 font-size,而全局基准通常是 html { font-size } 的设置。

注:如果一个元素的直接父元素没有设置 font-size,CSS 会遵循"向上查找"的规则,直到找到一个设置了 font-size 的祖先元素,或者最终使用浏览器的默认值(通常为 16px

还有人有疑问,我为什么非要使用em而不是px呢?我们上面说了em依赖浏览器默认字体大小,px 是绝对单位,始终与屏幕的物理像素直接对应。而用户可以在浏览器直接修改浏览器默认的字体大小,如果我们依据px那么效果呈现图可能过大或者过小不符合我们的预期,而em会进行动态适配。

viewport适配

当我们期望样式问题,最好可以直接通过css解决时还可以使用viewport

viewport视口 的意思,它表示浏览器的可视区域,也就是浏览器中用来显示网页的那部分区域。 我们可以通过meta标签来指定viewport

ini 复制代码
<meta name="viewport" content="width=device-width,initial-scale=1.0">

css还给我们提供了两个尺寸单位vwvh,它们两个都是相对于视口而言的,其中一个vw表示视口宽度的百分之一,一个vh表示视高度的百分之一。当屏幕尺寸发生变化时,vwvh也会同步发生变化,这样我们就不需要再通过js来控制了,而是可以直接通过vw单位来指定元素的fontsize,这样同样可以达到我们想要的效果

css 复制代码
div {
        font-size: 4vw
}

异形屏

现在的手机屏幕不一致,刘海屏、凹槽屏和全面屏等等,我们对于一些非常规屏称为异形屏。

我们在编写代码时元素可能会出现与曲面屏边缘/手机底部黑条/顶部刘海条/...重叠的情况,那样十分不美观且用户体验较差,所以我们应该把这块区域空出来,那么这块区域具体应该是多少呢? 想要知道这个我们需要先了解一个概念:安全区域

安全区域

安全区域指的是一个可视窗口范围,处于安全区域的内容不受圆角、齐刘海、小黑条的影响。图示如下:

我们进行布局时,应该仅在安全区域中进行布局。想要完成这样的功能,我们需要借助:

  • 一个viewport属性
  • 四个距离变量
  • 两个CSS函数

viewport属性

我们需要为viewport添加viewport-fit属性,这个属性用于定义内容如何适应不同形状的屏幕。 有三个值:

  • contain:可视窗口完全包含网页内容
  • conver:网页内容完全覆盖可视窗口
  • auto:默认值,跟contain表现一致
ini 复制代码
<meta name="viewport" content="width=device-width,initial-scale=1.0,viewport-fit=cover">

viewport-fit=cover 主要用于 iPhone X 及其以后的设备,确保页面内容完全覆盖屏幕,即使设备有刘海或圆角。需要注意的是,viewport-fit=cover 主要对 iOS 设备有效,是专门为了适配 iPhoneX 及类似设备(如刘海屏设备)而诞生的,安卓设备可能不会有这种效果

四个距离变量

  • safe-area-inset-left:设备屏幕左侧的安全区域边距。

  • safe-area-inset-right:设备屏幕右侧的安全区域边距。

  • safe-area-inset-top:设备屏幕顶部的安全区域边距。

  • safe-area-inset-bottom:设备屏幕底部的安全区域边距

两个CSS函数

我们使用两个CSS函数配合这四个变量来使用:

  • constant(safe-area-inset-); / 兼容 iOS<11.2 */
  • env(safe-area-inset-top); /* 标准语法 */

然后我们通过在 CSS 中设置这些值,以确保内容不会进入安全区域。例如:

css 复制代码
div {
  position: fixed;
  top: constant(safe-area-inset-top); 
  top: env(safe-area-inset-top);
  left: env(safe-area-inset-left);
  right: env(safe-area-inset-right);
  bottom: env(safe-area-inset-bottom);
}

好了以上就是本文所有内容,完结,撒花🎉

相关推荐
腾讯TNTWeb前端团队44 分钟前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰4 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪4 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪4 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy5 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom6 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom6 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom6 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom6 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom6 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试