移动端布局适配的方案

2022-12-31 0 929



移动端布局适配的方案

视口

视口(viewport)就是可视区域的大小。在前端页面布局中,一共有三种类型的视口:理想视口(ideal viewport),布局视口(layout viewport),视觉视口(visual viewport). 需要注意的是,在PC端,我们一般只有一个视口,就是pc浏览器的大小,而在移动端,采用上面的三种视口。

1. 布局视口

也就是我们html文档整个页面的布局大小(网页的宽度)。当我们以百分比来指定一个元素的大小时,它的计算值是由这个元素的包含块计算而来的。当这个元素是最元素时,它就是基于布局视口来计算的。

移动端布局适配的方案在PC浏览器上,布局视口就等于当前浏览器的窗口大小(不包括borders, margins, 滚动条)。 在移动端,布局视口默认为980px,这保证PC的网页可以在手机浏览器上呈现,但是非常小,用户可以手动进行放大。

获取方式

document.documentElement.clientWidth/clientHeight
复制代码

2. 视觉视口

就是我们人眼能看到的可视区域大小,默认等于浏览器的宽度。

移动端布局适配的方案

当用户对浏览器进行缩放时,不会改变布局视口的大小,所以页面布局是不变的,但是缩放会改变视觉视口的大小。 比如:将浏览器窗口放大200%,css像素会随着视觉视口的放大而放大,这时一个css像素会跨越更多物理像素

小结:布局视口会限制你的CSS布局,视觉视口决定用户具体能看到什么。

获取方式:

window.innerWidth/innerHeight
复制代码

3. 理想视口:

布局视口在移动端展示的效果并不是一个理想的效果,因为不同屏幕大小的移动设备视觉视口都不一样,而布局视口默认都是固定的,因此展示效果不理想,所以出现了理想视口,网页在移动端展示的理想大小

移动端布局适配的方案页面的缩放系数 = CSS像素/设备独立像素, 实际上 = 理想视口宽度/视觉视口更准确。当页面缩放比例为100%时,CSS像素 = 设备独立像素,理想视口 = 视觉视口

获取方式:

screen.width/height
复制代码

获取浏览器大小的常用api

上面已经介绍过三种视口的大小的获取方式,其实在实际应用中,还有一些其他的常见api。

  • window.innerHeight: 获取视觉视口的高度(包括滚动条)
  • window.outerHeight: 获取浏览器窗口外部的高度,表示整个浏览器窗口的高度,包括侧边栏、窗口镶边和调正窗口大小的边框。
  • window.screen.height: 获取屏幕理想视口的高度,这个数值是固定的,等于设备的分辨率/设备的像素比
  • window.screen.availHeight: 浏览器窗口可用的高度
  • document.documentElement.clientHeight: 获取布局视口的高度,包括内边距,但不包括滚动条,边框和外边距
  • document.documentElement.offsetHeight: 获取布局视口的高度,包括内边距,滚动条,边框和外边距
  • document.documentElement.scrollHeight: 在不使用滚动条的情况下适合视口中的所有内容所需的最小宽度。与clientHeight对应,包括滚动条,边框和外边距

常见的布局适配方案

1. 设置视口

基于上面对视口的介绍,我们在适配浏览器的时候,通常需要在meta中给viewport设置视口和缩放,从而达到理想视口的情况。通常设置如下:

<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
复制代码

配置说明:

  • width:以正整数或像素为单位,定义布局视口的宽度。这里我们设置width=device-width就是让布局视口的宽度等于设备宽度。也就是让布局视口等于视觉视口
  • initial-scale: 0.0 – 10.0, 定义页面初始缩放比率。
  • minimum-scale: 0.0 – 10.0, 定义缩放的最小值。
  • maximum-scale: 0.0 – 10.0 定义缩放的值。
  • user-scalable:布尔值(yes或者no),如果设置为 no,用户将不能放大或缩小网页。默认值为 yes。这几个缩放的属性是用于不让用户缩放,这样布局视口就会一直等于视觉视口

需要注意的一点是:我们在做移动端做适配的时候,不仅是只有宽度需要适配,盒子的高度,边框,内外边距以及font-size都要适配。

2. 百分比

百分比这种就是将每一个盒子的宽高等都设置成百分比,这样不同设备的布局视口不一样,每次都是根据百分比来计算,也能得到适配的效果。

优点

  1. 上手容易,适配方案易理解

缺点

  1. 不同的css属性的百分比所基于的标准不一样
  2. 每个属性都需要手动计算百分比,很麻烦
3. rem

rem是相对单位。rem适配方案是基于html的font-size来计算的,即1rem = html的font-size。不论在什么屏幕下,css属性的rem是不变的,变化的是html的font-size,这样最后浏览器将rem转成px渲染出来也就能达到一个适配不同屏幕啦。 如: 设计稿以750px为标准, 某个盒子的宽:300px, 高: 300px 按照设计稿的编码: html的font-size = 1rem = 750/10 = 75px 盒子的width = 300/750*10 = 4rem

屏幕宽度 html的font-size 盒子的宽度
640px 640/10 = 64px 64*4 = 264px
480px 480/10 = 48px 48*4 = 192px
320px 320/10 = 32px 32*4 = 128px

上述html的font-size(也就是rem)需要根据布局视口的大小进行改变,这一步一般有两种方法:

3.1. rem的实现方式

3.1.1 自己实现js动态转换

3.1.1.1 首先要根据设计稿的尺寸将元素的格式都转成rem

我们这里采用的公式如下: 1rem = 设计稿的尺寸 / 0 元素的width = 设计稿上元素的宽度 / 设计稿的尺寸 * 10

这里我们可以写一个px转成rem的函数,以scss为例, 设计稿尺寸为750。

 @function rem($px) {
   @return $px / 750 * 10rem;
 }
复制代码

3.1.1.2 将上述函数引入全局

这里我们以vue项目为例,将上述函数所在的scss文件引入main.js中

// main.js中
import '@scss/index.scss';
复制代码

3.1.1.3 在页面中使用rem

设计稿是什么尺寸大小,函数里的参数就是什么值

// 比如750设计稿里的width是300
width: rem(300);
复制代码

3.1.1.4 根据屏幕动态设置html的font-size

上述步骤只是实现了设计稿的尺寸下rem为单位的正常显示,现在需要根据不同屏幕动态设置html的font-size。

function setRem() {
 const htmlEl = document.documentElement;
 const htmlWidth = htmlEl.clientWidth;
 const htmlFontSize = htmlWidth / 10;
 htmlEl.style.fontSize = htmlFontSize + 'px';
};
// 次进入页面调用
setRem();
window.addListenner('resize', setRem);
复制代码

实现了上述逻辑之后,我们就可以实现rem适配不同屏幕了。

当然这一步也可以使用媒体查询去根据不同尺寸的屏幕设置html的font-size.

 /* pc width > 1100px */
 html{ font-size: 20px;}
 /* ipad pro */
 @media screen and (max-width: 1024px) {
   html{ font-size: 18px;}
 }
 /* ipad */
 @media screen and (max-width: 768px) {
   html{ font-size: 16px;}
 }
 /* iphone6 7 8 plus */
 @media screen and (max-width: 414px) {
   html{ font-size: 15px;}
 }
 /* iphoneX */
 @media screen and (max-width: 375px) and (-webkit-device-pixel-ratio: 3) {
   html{ font-size: 14px;}
 }
 /* iphone6 7 8 */
 @media screen and (max-width: 375px) and (-webkit-device-pixel-ratio: 2) {
   html{ font-size: 13px;}
 }
 /* iphone5 */
 @media screen and (max-width: 320px) {
   html{ font-size: 12px;}
 }

复制代码

3.1.2. 使用插件

如何是使用插件的话,主要会用到两个插件:

  • lib-flexible: 这个插件主要是用于将适配不同屏幕,设置对应的html的font-size。
  • px2rem-loader: 这个插件主要是用来将页面上设置的样式的px转成rem, 也就是我们上面写的rem函数。

使用步骤:

3.1.2.1 安装插件

npm i lib-flexible px2rem-loader --save-dev
复制代码

3.1.2.2. 配置

main.js中引入lib-flexible

// 引入lib-flexible
import 'lib-flexible/flexible';
复制代码

vue.config.js中配置px2rem-loader

// 在loaderOptions中添加如下配置
postcss: {
  plugins: [
    require('px2rem-loader')({
      remUnit: 75
    })
  ]
}
复制代码

特别需要注意的是:remUnit: 75这里的75是相对于设计稿给的是750的尺寸,如果设计稿给的是375,那么这里的值为37.5.

3.1.2.3. 使用

重启项目后我们就可以写我们的样式了。

特别注意:

1) postcss-px2rem和postcss-pxtorem的区别

场景:我的项目中安装了autoprefixer,安装postcss-px2rem插件后项目启动报错:node.getIterator is not a function postcss

原因:postcss-px2rem插件在全局引入时,main.js导入样式文件报错。

解决方案:卸载postcss-px2rem, 安装postcss-pxtorem.然后在postcss.config.js中按照如下配置:

// 安装postcss-pxtorem
npm install postcss-pxtorem --save-dev
复制代码
// postcss.config.js
const autoprefixer = require('autoprefixer')
const px2rem = require('postcss-pxtorem')

module.exports = {
  plugins: [autoprefixer(), px2rem({ rootValue: 75, unitPrecision: 5, propList: ['*'] })]
}
复制代码

参数配置如下:

  • rootValue: 换算基数,即根元素字体大小。如何设计稿是750,这里设置成75,如果设计稿是375, 这里设置成37.5
  • unitPrecision: 允许rem单位增长的十进制数
  • propList: 可以从px更改为rem的属性

2)lib-flexible和amfe-flexible

amfe-flexible是lib-flexible的升级方案 lib-flexible是一种过渡方案,不能与响应式布局兼容。由于viewport单位得到众多浏览器的兼容,上面这种方案现在已经被官方弃用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用viewport来替代此方。amfe-flexible的缺点是字体大小适配问题。

flexible的核心代码

// 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()
    }
})
复制代码

3.2 优缺点

优点:可以根据屏幕显示合适的大小,能够适配不同屏幕 缺点:

  • 在响应式布局中,必须通过js来动态控制根元素font-size的大小,也就是说css样式和js代码有一定的耦合性,且必须将改变font-size的代码放在css样式之前,如果改变系统的字体大小,页面会错乱
4. vw/vh

vw/vh这种方案是将视觉视口的宽度/高度分为100份,其实上面的flexible方案就是模仿这种方法,因为当时vm的兼容性不好,flexible就变成了一种过渡方案。

  • vw(viewport’s width): 1vw等于视觉视口宽度的1%
  • vh(viewport’s height): 1vh等于视觉视口高度的1%
  • vmin: vw和vh的最小值
  • vmax: vw和vh的值

如果设计稿的标准是375px, 某个元素的宽度是75px。那么1vw = 375 / 100 = 3.75px, 75 / 3.75 = 20vw,设置元素的宽度width: 20vw。 如果每个css属性都这样去计算的话工作量太大,这里也可以像rem一样,自己去手动计算,或者使用插件。

4.1 手动将px转化成vw

// index.scss
@function pxToVw($px) {
  @return $px / (750  / 100) + 'vw';
}
复制代码

4.2 使用插件postcss-px-to-viewport

4.2.1. 安装插件

npm install postcss postcss-loader postcss-px-to-viewport -D
复制代码

4.2.2. 配置插件

在postcss.config.js中配置以下代码

 plugins: {
   autoprefixer: {},
   "postcss-px-to-viewport": {
     unitToConvert: 'px', // 把什么单位转换成vw
     viewportWidth: 750, // 这个可以按照你的设计稿来设置,是750就设置750,375就设置成375
     unitPrecision: 6, // 转换成vw单位的小数点后的保留位数
     propList: ['*'], // 属性列表,表示你要把哪些css属性的px转换成vw,这个*表示所有
     viewportUnit: 'vw', // 使用的单位,目前可选单位有vw,vh。一般我们都有vw
     fontViewportUnit: 'vw', // 字体使用的单位
     selectorBlackList: [], // 匹配不被转换为vw的选择器
     minPixelValue: 1, // 需要转换的最小值,一般1px像素不转换,以上才转换
     mediaQuery: false, // 允许在媒体查询中转换px
     replace: true, // 替换包含vw的规则,而不是添加回退
     exclude: [], // 忽略一些文件,比如“node_modules”,可以是正则表达式
     landscape: false,  // ......
     landscapeUnit: 'vw', // ......
     landscapeWidth: 568 // ......
   }
 }
复制代码

4.2.3. 使用插件

安装完成之后重启项目我们就可以像以前一样使用px了,但是在浏览器中我们可以看到所有的单位都被转成了vw。 代码:

 .test_box {
   width: 300px;
   height: 300px;
   background-color: aquamarine;
 }
复制代码

浏览器效果: image.png

4.3 优缺点

优点:上手简单,易于理解

缺点:

  • px转换成vw不一定能整除,因此会有一定的像素差
  • 比如当容器使用vw,margin采用px时,很容易造成整体宽度超过100vw,从而影响布局效果。当然我们也是可以避免的,例如使用padding代替margin,结合calc()函数使用等等…

5. flex布局

flex布局主要是用来实现弹性布局,比如页面的多列的响应式布局,总之弹性布局是适配多种屏幕的响应式布局的一种补充。flex布局的内容可以参考我的另一篇文章:

在做移动端适配的时候如何选择

对于大部分主流浏览器来说,我们可以选择使用vw的方案(postcss-px-to-viewport)来实现移动端的适配,如果你所在公司项目需要适配很早之前的浏览器,那么你可以配合使用(amfe-flexible + postcss-pxtorem)的方案去适配。当然,这些适配方案能适配大多数屏幕,但是对于一些特殊的机型,比如刘海屏等可能会出现一些样式不正常的情况,对于这种情况,我们可以再进行特定的优化和调试。

比如,对于有圆角,刘海屏,小黑条等,为了适配这些手机,产生了安全区域的概念,安全区域指的就是不受前面这几种情况的影响,把页面限制在安全区域内,使页面显示正常。

特殊屏幕的问题

1. viewport-fit

这个属性就是为了适配上述的屏幕,用于限制网页在安全区域内进行展示。它一共有两个属性值

  • contain:视觉视口完全包含网页内容
  • cover: 网页内容完全覆盖视觉视口
<meta name="viewport" content="viewport-fit=cover">
复制代码

移动端布局适配的方案

2. env、constant

我们需要将顶部和底部合理地摆放在安全区域内,ios11新增了两个CSS函数env、constant, 用于设定安全区域与边界的距离。函数的内部可以是四个常量:

  • safe-area-inset-left: 安全区域距离左边边界距离
  • safe-area-inset-right: 安全区域距离右边边界距离
  • safe-area-inset-top: 安全区域距离顶部边界距离
  • safe-area-inset-bottom: 安全区域距离底部边界距离

使用方式

这个函数必须是指定viewport-fit之后才可以使用

<meta name="viewport" content="viewport-fit=cover">
body {
  // constant在iOS < 11.2的版本中生效,env在iOS >= 11.2的版本中生效
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
}
复制代码
常见问题

1. 1px问题

现象:在有的手机屏幕上,1px的大小看起来很粗

产生原因:在设备像素比大于1的屏幕上,我们写的1px实际上是被多个物理像素渲染。

解决方案

// 伪类 + transform
  .border_1px:before{
    content: '';
    position: absolute;
    top: 0;
    height: 1px;
    width: 100%;
    background-color: #000;
    transform-origin: 50% 0%;
  }
  @media only screen and (-webkit-min-device-pixel-ratio:2){
      .border_1px:before{
          transform: scaleY(0.5);
      }
  }
  @media only screen and (-webkit-min-device-pixel-ratio:3){
      .border_1px:before{
          transform: scaleY(0.33);
      }
  }
复制代码

2. 图片模糊问题

现象:我们平时使用的图片都是位图(PNG, JPG格式),在有些手机上显示很模糊

产生原因:位图的每个像素对应在屏幕上使用一个物理像素来渲染,,而在dpr > 1的屏幕上,位图上的一个像素对应屏幕上的多个物理像素来渲染,然而这些物理像素点并不能被准确的分配上对应位图像素的颜色,只能取近似值,所以相同的图片在dpr > 1的屏幕上就会模糊。

解决方案:

  1. 在dpr=2的屏幕上使用两倍图(@2x), 在dpr=3的屏幕上展示三倍图(@3x) 这里我们主要分为两种情况。图片是背景图片还是img标签1.1 背景图1.1.1 采用媒体查询的方式:
     .avatar{
         background-image: url(conardLi_1x.png);
     }
     @media only screen and (-webkit-min-device-pixel-ratio:2){
         .avatar{
             background-image: url(conardLi_2x.png);
         }
     }
     @media only screen and (-webkit-min-device-pixel-ratio:3){
         .avatar{
             background-image: url(conardLi_3x.png);
         }
     }
    复制代码

    1.1.2 使用-image-set

     .avatar {
       background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x );
     }
    复制代码

    1.2 img标签

    使用img的srcset属性,浏览器会根据像素密度匹配显示适当的照片

    <img src="conardLi_1x.png" srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x">
    复制代码
  2. 使用svg图片格式
     <img src="conardLi.svg">
    
     <img src="data:image/svg+xml;base64,[data]">
    
     .avatar {
       background: url(conardLi.svg);
     }
    复制代码

1. 本站所有资源来源于用户上传和网络,因此不包含技术服务请大家谅解!如有侵权请邮件联系客服!cheeksyu@vip.qq.com
2. 本站不保证所提供下载的资源的准确性、安全性和完整性,资源仅供下载学习之用!如有链接无法下载、失效或广告,请联系客服处理!
3. 您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源!如用于商业或者非法用途,与本站无关,一切后果请用户自负!
4. 如果您也有好的资源或教程,您可以投稿发布,成功分享后有积分奖励和额外收入!
5.严禁将资源用于任何违法犯罪行为,不得违反国家法律,否则责任自负,一切法律责任与本站无关

源码下载

发表评论
暂无评论