继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

PC前端转移动前端的二三事——移动端适配

BIG阳
关注TA
已关注
手记 429
粉丝 70
获赞 457

一、首先,要搞明白移动端适配问题,就要先搞明白像素和视口

像素包含2种像素:物理像素和css像素

物理像素又称设备像素,任何设备的物理像素的数量是固定不变的,单位是pt。所谓的一倍屏、二倍屏(Retina)、三倍屏,指的是设备以多少物理像素来显示一个CSS像素。

CSS像素就是我们写CSS时所用的像素,是一个抽像的单位,在不同的设备或者不同的环境中,css中的1px所代表的设备物理像素是不同的。比如早期的iphone3的分辨率是 320px*480px,1css像素=1物理像;iphoen4开始分辨率提高成了640px*960px,但屏幕尺寸没变,意味着同样大小的屏幕上,像素多了一倍,此时1css像素 = 2物理像素.

设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系,前提条件是在缩放程度为100%

dpr = 设备像素 / CSS像素

可以通过JS得到: window.devicePixelRatio

以iphone6为例:iphone6的设备宽和高为375pt * 667pt,即为设备的物理像素,而其设备像素比为2.固css像素为750pt * 1334pt

关于viewpoint的基本概念,可以参考文章

viewpoint严格等于浏览器的窗口;viewpoint不是一个html的概念,不能通过css修改它

视觉视口

就是用户看到的网页的区域,window.innerWidth/Height 返回视觉视口的尺寸

webp

image.png

布局适口

布局视口不是指设备屏幕区域,它是浏览器厂商定的视口,为了解决PC端网站在移动端展示不佳的一个解决方案,通常比设备屏幕宽得多,一般为980px,但也不是唯一,在不同的浏览器中也会有所不同如:在Safari iPhone中布局视窗的宽为980px,在Opera中布局视窗宽为850px。

可以通过document.documentElement.clientWidth /Height 来获取

你有可能实验过,然后说“你说的不对啊”,不要着急,你的页面可能添加了meta viewport,并且 设置了width = device-width,如果那样通过上面的代码所获得的值就不是布局视窗的默认值了。

webp

image.png

理想视口

布局视口明显对用户是不友好的,完全忽略了手机本身的尺寸。所以苹果引入了理想视口的概念,把布局视窗调整到合适的状态,让页面有最好的表面效果。设置了理想视窗用户就不再需要对页面进行缩放,因为浏览器已经帮你把页面调整到最佳的显示状态了。而你要做的就是告诉浏览器,你要他这么做就OK了。用代码实现就是

<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

PS: device-width 是设备宽度,initial-scale是缩放比例, user-scalable 是是否允许用户缩放

你明白了吗?赶紧动动手试试吧。

移动端适配

1、什么是适配?

由于手机型号多,把设计图实现在各个手机上的过程就是适配

2、怎么做?

我这里说4种方案:

  • 固定高度,宽度自适应

  • 固定宽度,viewport缩放

  • rem做宽度,viewport缩放

  • 使用vw

方案一:固定高度,宽度自适应

是目前使用最多的方法,垂直方向用定值,水平方向用百分比、定值、flex都行。随着屏幕宽度变化,页面也会跟着变化,效果就和PC页面的流式布局差不多

这种方法使用了理想视口:

<meta name="viewport" content="width=device-width,initial-scale=1">

这样设置之后,我们就可以不用管手机屏幕的尺寸进行开发了。

方案二 :固定宽度,viewport缩放

如:荔枝FM网易应用

荔枝的代码:

if(/Android (\d+\.\d+)/.test(navigator.userAgent)){  var version = parseFloat(RegExp.$1);  if(version>2.3){    var phoneScale = parseInt(window.screen.width)/640;    if(/MZ-M571C/.test(navigator.userAgent)){      document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">');
    }else if(/M571C/.test(navigator.userAgent)&&/LizhiFM/.test(navigator.userAgent)){      document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">');
    }else{      document.write('<meta name="viewport" content="width=640, minimum-scale = '+ phoneScale +', maximum-scale = '+ phoneScale +', target-    densitydpi=device-dpi">');
    }
  }else{    document.write('<meta name="viewport" content="width=640, target-densitydpi=device-dpi">');
  }
}else{  document.write('<meta name="viewport" content="width=640, user-scalable=no, target-densitydpi=device-dpi">');
}

固定布局视口,宽度设置固定的值,总宽度为640px,根据屏幕宽度动态生成viewport。(假设设计稿是640px的)

这种方式布局如荔枝FM的网页宽度始终为640px。缩放比例scale为:var scale = window.screen.width / 640

方案三:rem做宽度,viewport缩放

这也是淘宝使用的方案,根据屏幕宽度设定 rem 值,需要适配的元素都使用 rem 为单位,不需要适配的元素还是使用 px 为单位。

具体使用方法见使用Flexible实现手淘H5页面的终端适配

实际上做了这几件事情:

  1. 动态生成 viewport

  2. 屏幕宽度设置 rem的大小,即给<html>设置font-size

  3. 根据设备像素比(window.devicePixelRatio)给<html>设置data-dpr

设置 viewport 缩放 和 data-dpr

这两个设置其实是干的一件事,就是适配高密度屏幕手机的px单位。

根据设置的dpr设置font-size

document.documentElement.style.fontSize = 50 * dpr;// dpr 为设置的设备像素比。(注意不是设备自身的设备像素比,而是认为设置的dpr)

当设计以iphone6为标准,出750px的设计稿时,此时dpr=2,故1rem 等于100px,将图上的尺寸转换为rem非常方便,除以100就行。代码如下:

var scale = 1.0;var dpr = 1;var isAndroid = window.navigator.appVersion.match(/android/gi);var isIPhone = window.navigator.appVersion.match(/iphone/gi);var devicePixelRatio = window.devicePixelRatio;// 此处只简单对ios做了伸缩处理,安卓没有做伸缩处理,统一dpr = 1if ( isIPhone ) {
  scale /= devicePixelRatio;
  dpr *= devicePixelRatio;
}var viewport = document.getElementById('viewport');var content = 'initial-scale=' + scale + ', maximum-scale=' + scale + ',minimum-scale=' + scale + ', width=device-width, user-scalable=no';
viewport.setAttribute( 'content', content );document.documentElement.style.fontSize = 50 * dpr + 'px';document.documentElement.setAttribute('data-dpr', dpr);

对于该方案,假设肉眼看到的宽度(视觉宽度):visualWidth,令dpr=1时,其1rem对应的宽度为50.

dpr = 1 时, 1rem = 50px, initial-scale=1, 缩放为1。
visualWidth = 50 * 1 = 50;

dpr = 2 时, 1rem = 100px, initial-scale=0.5, 缩放为0.5。
visualWidth = 100 * 0.5 = 50;

dpr = 3 时, 1rem = 150px, initial-scale=0.3333, 缩放为0.3333。
visualWidth = 150 * 0.3333 = 50

也许你会有疑问,这种先设置成倍数尺寸,又缩放是不是多此一举,反正我当时看的时候就比较懵逼;

继续研究了下发现这是为了解决 retina下,图片高清问题

我先具体描述下这个问题:

理论上,1个位图像素对应于1个物理像素,图片才能得到完美清晰的展示。

在普通屏幕下是没有问题的,但是在retina屏幕下就会出现位图像素点不够,从而导致图片模糊的情况。

用一张图来表示:

webp

image.jpeg

如上图:对于dpr=2的retina屏幕而言,1个位图像素对应于4个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊(注意上述的几个颜色值)。

所以,对于图片高清问题,比较好的方案就是两倍图片(@2x)。

如:200×300(css pixel)img标签,就需要提供400×600的图片。

如此一来,位图像素点个数就是原来的4倍,在retina屏幕下,位图像素点个数就可以跟物理像素点个数形成 1 : 1的比例,图片自然就清晰了(这也解释了之前留下的一个问题,为啥视觉稿的画布大小要×2?)。

所以这个问题的解决方案就是:两倍图片(@2x),然后图片容器缩小50%。

方案4:使用vw

目前,vw已经得到了众多浏览器的支持,所以可以直接考虑将vw单位运用于适配布局中,这也是我们项目目前在使用的方案

原理:假设设计稿为750px,那么100vw = 750px, 1vw = 7.5px,那么就可以根据px直接转换成vw了,为了计算方便,可以使用PostCss的插件postcss-px-to-viewport,这样可以直接在代码里写px,比如:

div{  width: 30px;
}

编译之后就是我们需要的带vw的代码

div{  width: 4vw;
}

使用的时候,可以对该插件进行参数配置:

{
  viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750
  viewportHeight: 667, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置
  unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)
  viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用 vw
  selectorBlackList: ['.ignore', '.hairlines'], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
  minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
  mediaQuery: false // 允许在媒体查询中转换`px`著作权归作者所有。}

在不想把px转换成vw的时候,在元素中添加类名 .ignore 或者 .hairlines (.hairlines 一般用于设置 0.5px border)

Retina下1px的问题
可以使用postCss插件postcss-write-svg

@svg 1px-border{  height: 2px;
  @rect{    fill: var(—-color, black);    width: 100%;    height: 50%;
  }
}.example{  border: 1px solid transparent;  border-image: svg(1px-border param(—color #00b1ff)) 2 2 stretch;
}

编译出来之后

.example{  border: 1px solid transparent;  border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='[http://www.w3.org/2000/svg](http://www.w3.org/2000/svg)' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") ;
}

也可以使用border-image的方式

@svg square{
  @rect{    fill: var(--color, black);       width: 100%; 
       height: 100%;
  }
}

附注:

名称方法度量说明示例移动端
屏幕尺寸 screen sizescreen.width / height设备pixels用户屏幕的完整大小,不重要

webp

image.jpeg


浏览器尺寸 window sizeWindow.innerWidth / innerheightcss pixels[当zoom in放大的时候,数值变小]包含滚动条尺寸的浏览器完整尺寸

webp

image.png

webp

image.jpeg

滚动位移 scrolling offsetwindow.pageXOffset / pageYOffsetcss pixels[当zoom in放大的时候,数值不变]页面相对于窗口原点的水平/垂直位移

webp

image.jpeg


视口viewpointwindow.documentElement.clientWidth / cilentHeightcss pixels视窗内的css pixels

webp

image.jpeg

webp

image.jpeg

<html> 尺寸window.documentElement.offsetWidth / offsetHeightcss pixelshtml元素的尺寸

webp

image.jpeg



Event.pageX / Y[使用较多]CSS pixels从<html>原点到事件触发点的距离

webp

image.jpeg



Event.clientX / YCSS pixels从viewport原点(浏览器窗口)到事件触发点的距离

webp

image.jpeg



Event.screenX / YCSS pixels从用户显示器窗口原点到事件触发点的距离

webp

image.jpeg





作者:Wendy曹
链接:https://www.jianshu.com/p/aa60a9100bf1


打开App,阅读手记
2人推荐
发表评论
随时随地看视频慕课网APP