移动端适配rem/vw,vh原理(详细)

7,088 阅读8分钟

了解像素相关知识:
移动端适配
掌握web开发基础系列--物理像素、逻辑像素、css像素
为什么给750px的设计图

其实我一直很想写一些关于适配方面的文章,因为很多东西只有实践才能对这个知识点更加的了解,而不是查几篇文章就行了~

我希望能够解答这些问题:

1.什么是物理像素,和逻辑像素,DPR
2.为什么我们的设计师给的尺寸都是750px
3.rem适配原理以及对应的插件
4.vw,vh适配原理以及对应的插件

1.什么是物理像素,和逻辑像素,DPR

物理像素(physical pixel, 也叫做设备像素):显示器上的像素,机器生产出来时就已经确定了。
逻辑像素(density-independent pixel也叫做css像素/设备独立像素):用于控制元素样式的样式单位像素,CSS像素从来都只是一个相对值。

物理像素(physical pixel): 设备屏幕实际拥有的像素点,屏幕的基本单元,是有实体的。比如iPhone 6的屏幕在宽度方向有750个像素点,高度方向有1334个像素点,所有iPhone 6 总共有750*1334个像素点。

逻辑像素/独立像素(density-independent pixel): 我们平时描述一张图片宽高时一般用 200px * 100px,这里的px也是逻辑像素。

我们应该知道iphone6的宽度是375px,这里指的是逻辑像素,那就说明1px的css像素里面包含了2个物理像素点,如果你不怕瞎的话,可以将眼睛靠近电脑屏幕,发现屏幕的界面都是根据红、绿、蓝三个颜色组成

以前的的屏幕是假设是375px的物理像素,对应的是375个物理像素点,其实在很久以前,CSS里写个1px,屏幕就给你渲染成1个实际的像素点,DPR=1,多么简单自然~ 后来苹果公司为其产品mac、iPhone以及iPad的屏幕配置了Retina高清屏,也就是说这种屏幕拥有的物理像素点数比非高清屏多4倍甚至更多。如果还按照DPR=1进行展示,那么同一张图片在高清屏上面显示的区域面积会是非高清屏的1/4,这样的话由于图片在屏幕上的展示面积大大缩小,也会导致出现“看不清”的问题。

在1px对应多个物理像素点主要是为了更加高清,也就是越多,对于我们肉眼能看到红、绿、蓝三个色的难度越大. 所以当你使用750px的设计图的时候,对应的图应该是xx2@png

设备像素比(Device Pixel Ratio,DPR)

DPR = 物理像素/逻辑像素

2.rem 适配

2.1 CSS3 长度单位 rem

image.png

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  我是根元素html的大小
  <div class="two">我是两倍</div>
  <div class="three">我是三倍</div>
  
  <style>
    html{ 
      font-size:12px;
    }
    .two { 
      font-size:2rem;
      height: 5rem;
      width: 5rem;
      background-color: red;
    }
    .three{
      font-size:3rem;
      height: 10rem;
      width: 10rem;
      background-color: yellow;
    }
  </style>
</head>
</html>

image.png

比如上面这个例子,虽然我只设置了font-size:12px;,并没有去设置width是多少,但是rem就是看html的font-size的值为固定值,在这个基础上去乘以对应的倍数,其实我这块我以前老是转不过弯,为啥这么多属性就只设置一个font-size的默认值就行,主要还是看rem初始的定义,也许这个属性不是font-size,也许是test等等,他规定什么属性就是什么属性,我是这么理解的,但是font-size的默认值和最小值值得注意

font-size:2rem; => font-size:2*12px;

那么,思考一下,这个html的font-size的值我到底设置多少呢?是不是应该根据视觉宽度来设定

2.2 浏览器默认字体大小

浏览器默认最小字体12px, 意思就是我设置一个字体为2px,这个2px,是不会起作用的,会显示城最小的字体12px 默认的字体大小是16px,意思就是假设我不会html做任何设置,对其他的样式class设置2rem,那么显示出来的样式应该是32px(16px * 2)

如何获取google浏览器的默认font-size

var div=document.getElementsByTagName('html')[0]
const res = window.getComputedStyle(div).fontSize // 16px
console.log(res, 1111)

image.png

1625394225(1).png

2.3 rem适配

假设我们的UI图给的尺寸是375px的逻辑像素

     .two { 
      font-size:2rem; 
      height: 5rem;
      width: 5rem;
      background-color: red;
    }
    .three{
      font-size:3rem;
      height: 10rem;
      width: 10rem;
      background-color: yellow;
    }
获取原始值,html的默认font-size为16,意思就是在iphone6下面
375px的UI设计图之下,量出来的应该为:
2rem = 32px 5rem = 80px 3rem = 48px 10rem = 160px
750px的UI设计图,量出来的应该为:
2rem = 64px 5rem = 160px 3rem = 96px 10rem = 320px
适配代码分析

innerWidth 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏)
innerHeight 浏览器窗口可视区高度(不包括浏览器控制台、菜单栏、工具栏)

//原理:
// 获取屏幕的宽度,然后将屏幕分为多少份(设计稿宽度那么多份)
const boxInnerWidth = window.innerWidth
// 假设设计稿为375px
const UIwidth = 375
// 每份分成多少px
const everyPiece = boxInnerWidth/UIwidth 
// 将html的font-size设置为比例值
document.documentElement.style.fontSize = `${everyPiece * 100}px`

改成以下也可以:

(function () {
   function changeRootFont() {
      var UIwidth = 750, rem2px = 100;
      document.documentElement.style.fontsize = 
     ((window.innerWidth / UIwidth) * rem2px) + 'px';
   }
   changeRootFont();
   window.addEventListener('resize', changeRootFont,false);
})();

按道理来说,document.documentElement.style.fontSize = ${everyPiece}px就行了呀, 为什么要*100,假设我们在iphone6手机里,那么const everyPiece = boxInnerWidth/UIwidth的everyPiece会等于1px,html的font-size设置为1是不会生效的,所以我们需要将font-size扩大到12倍以上,但是你想这里我扩大了,等我写rem倍数的时候,是不是要除以这个对应的扩大倍数,所以为了好除,我们直接写100,也就是我们量出来的ui里面的px数往右边移动两个小数点就好了

.two { 
  font-size:0.32rem; 
  height: 0.8rem;
  width: 0.8rem;
  background-color: red;
}
.three{
  font-size:0.48rem;
  height: 1.6rem;
  width: 1.6rem;
  background-color: yellow;
}

3.vw,vh适配

image.png

100vw = 一个视口的宽度
1vw = 1%视口的宽度
100vh = 一个视口的高度
1vh= 1%视口的高度
 <div class="test">
   哈哈哈哈
  </div>
.test {
  width: calc(100vw * (300 / 375));
  height: 100px;
  background-color: red;
  color: white;
  font-size: calc(100vw * (24 / 375)) ;
}

假设你在项目中,可以使用sass

@function px2vw($px) {
  @return $px * 100vw / 375; // 375的设计图
}

.test {
  width: px2vw(300);
  height: px2vw(100);
  background-color: red;
  color: white;
  font-size: px2vw(24);
}

原理: 因为vw是将屏幕分成100份,1vw表示其中的一份,100vw表示的是横向满屏,那么假设我的设计图是375px,目前量的宽是24px,那么 24/375表示是我占屏幕总宽度的24/375,这时候你又知道 屏幕总宽度为100vw, 那么动态的宽度为24/375*100vw,而24的尺寸不一,所以封装一个方法来调用

VW,rem的优缺点:

rem:

1.因为是根据html根元素来做其他元素的rem处理的,所以script的代码应该在定义其他元素css之前,必须写在header里面,在触发resize的时候还要再获取window.innerWidth
2.如果是设置的${everyPiece * 100}px这里的比例值不是100,px转成rem会很难算

vw:

1.vw单位兼容性比rem稍差,ios8、安卓4.4及以上才完全支持
2.假设上面这样使用一个方法,写法上看起来我觉得有点怪怪的,没有rem那么直接

所以有了结合版本:(大佬说他就是用的这个,没有任何问题)
html{
  font-size:calc(100vw/375*100)
}
.test {
  width: 3rem ;
  height: 1rem;
  background-color: red;
  color: white;
  font-size: 0.24rem
}

4.插件推荐(VUE.JS)

rem (fexible.js,2年前的项目用过)

参考:
fexible的使用
vue项目中使用lib-flexible解决移动端适配的问题

vw (postcss-px-to-viewport,目前在用)

参考(大家可以找个简单的文章看看哈,其实就是个插件):
如何在Vue项目中使用vw实现移动端适配

5.为什么设计师出750px的设计图

参考:
为什么设计稿是750px

看了很多的文章,大概的意思就是设计图是按照物理像素给的图,这块我也没有说彻底弄懂,哭

6.如果有手机端和pc端如何进行适配
const isMobile = function () {
  const userAgentInfo = navigator.userAgent
  let mobileAgents = [
    'Android',
    'iPhone',
    'SymbianOS',
    'Windows Phone',
    'iPad',
    'iPod',
  ]
  return mobileAgents.some(i => userAgentInfo.includes(i))
}

设置全局的isMobileFunc是否为手机端的判断,路由指向同一个页面,这个页面引入pc端的代码,以及mobile的代码,根据isMobile判断显示哪个子组件

适配: var UIwidth = isMobile ? 750 : pcUIWidth, rem2px = 100;

(function () {
   function changeRootFont() {
      var UIwidth = isMobile ? 750 : pcUIWidth, rem2px = 100;
      document.documentElement.style.fontsize = 
     ((window.innerWidth / UIwidth) * rem2px) + 'px';
   }
   changeRootFont();
   window.addEventListener('resize', changeRootFont,false);
})();

BTY,关于rem,vw适配更多的优缺点,欢迎评论,后续会将fexible.js和postcss-px-to-viewport的插件使用贴出来

-_-奥利给