运动端适配方案,多终端的适配实施方案
分类:前端技术

移动端适配方案(下)

2017/01/25 · CSS · 移动端

本文作者: 伯乐在线 - risker 。未经作者许可,禁止转载!
欢迎加入伯乐在线 专栏作者。

上一篇介绍了像素和视口这些基本概念,现在接着移动端的适配方案。

推荐一篇文章:MobileWeb适配总结,里面说到的三种布局方法已经说的很详细,还分别做了demo,我就不做了,这里说说三种方案的原理以及我使用中的感受,希望各为互补,大家理解是最重要的。

之前做过PC页面的人聊的最多的就是『兼容』,这是因为浏览器之间的差异引起的,不再多说。而移动端是基本没有『兼容』的问题的,全是CSS3,简直不要太开心。可是『适配』问题随之而来。

什么是『适配』?做PC页面的时候,我们按照设计图的尺寸来就好,这个侧边栏200px,那个按钮50px的。可是,当我们开始做移动端页面的时候,设计师给了一份宽度为640px的设计图。那么,我们把这份设计图实现在各个手机上的过程就是『适配』。

那么,我们怎么开始呢?目前有三种方法:

  • 固定高度,宽度自适应
  • 固定宽度,viewport缩放
  • rem做宽度,viewport缩放

这三种方法的核心都是视口的确定,现在以实现这个设计图为例说明。

图片 1

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

在移动互联网快速发展的今天,手机的种类和尺寸越来越多,作为前端的小伙伴们可能会越来越头疼,但又不得不去适配一款又一款的新机型。对于移动端适配,不同的公司、不同的团队有不同的解决方案。我在项目中也用了一部分解决方案,也看到了一些解决方案,对比下,总结一些自己的理解,希望对各位有帮助,找到最适合你们项目的适配方案。

前言

曾几何时,互联网到了移动时代,前端也不用为了兼容IE低版本浏览器而头痛。有了gulp,mv*等利器之后,前端开发似乎变得简单起来了。

可是最后为了处理各终端的适配而乱了手脚。

虽然H5的页面与PC的Web页面相比简单了不少,但是我们要想尽办法让页面能适配众多不同的终端设备。
看看下图你就会知道,这是多么痛苦的一件事情:

图片 2

Device metrics

固定高度,宽度自适应

demo

这也是目前使用最多的方法,垂直方向用定值,水平方向用百分比、定值、flex都行。腾讯、京东、百度、天猫、亚马逊的首页都是使用的这种方法。

随着屏幕宽度变化,页面也会跟着变化,效果就和PC页面的流体布局差不多,在哪个宽度需要调整的时候使用_响应式布局_调调就行(比如网易新闻),这样就实现了『适配』。

原理

这种方法使用了完美视口:

XHTML

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

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

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

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

下面是一些基础概念的讲解,帮助理解各种适配方案实现。

希望这篇文章可以对大家实现多终端适配的提供一些帮忙!

固定宽度,viewport缩放

demo

设计图、页面宽度、viewport width使用一个宽度,浏览器帮我们完成缩放。单位使用px即可。

目前已知荔枝FM、网易新闻在使用这种方法。有兴趣的同学可以看看是怎么做的。

原理

这种方法需要根据屏幕宽度来动态生成viewport,生成的 viewport 基本是这样:

XHTML

<meta name="viewport" content="width=640,initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5,user-scalable=no">

1
<meta name="viewport" content="width=640,initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5,user-scalable=no">

640 是我们根据设计图定下的,0.5 是根据屏幕宽度动态生成的。

生成的viewport告诉浏览器网页的布局视口使用 640px,然后把页面缩放成50%,这是绝对的等比例缩放。图片、文字等等所有元素都被缩放在手机屏幕中。

这个gif图说明了一切:

图片 3

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

像素:

设计需要配合什么

在h5产品开发的时候,设计师通常会选择iPhone6作为基准设计尺寸,交付给前端的设计尺寸是按750px * 1334px为准(高度会随着内容多少而改变)。
前端开发人员通过一套适配规则自动适配到其他的尺寸。

rem做宽度,viewport缩放

demo

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

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

上文提供了sass和postcss的px2rem转换方法,我这里给出less的px2rem。因为less不支持函数,所以需要安装插件 less-plugin-functions ,然后就简单了:

Sass

.function{ .px2rem(@px,@base:72px){ return: @px / <a href="; * 1rem; } }

1
2
3
4
5
.function{
    .px2rem(@px,@base:72px){
        return: @px / <a href="http://www.jobbole.com/members/base1470">@base</a> * 1rem;
    }    
}

这样使用:

Sass

div{ width: px2rem(100px); }

1
2
3
div{
    width: px2rem(100px);
}

使用这个方法的库:

  • lib-flexible
  • hotcss

原理

实际上做了这几件事情:

  1. 动态生成 viewport
  2. 屏幕宽度设置 rem的大小,即给<html>设置font-size
  3. 根据设备像素比(window.devicePixelRatio)给<html>设置data-dpr

图片 4

这么设置的好处是可以让不同设备的rempx都显示一样的长度。

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

1、物理像素(设备像素)

屏幕的物理像素,又被称为设备像素,他是显示设备中一个最微小的物理部件。任何设备屏幕的物理像素出厂时就确定了,且固定不变的。

前端开发完成终端适配方案

拿到设计师给的设计图之后,剩下的事情是前端开发人员的事了。

前端的解决方案通常是用自适应(Flex,百分比) rem.

更高端一点的办法就是通过Iconfont或者svg来处理Icon,用bootstrap的栅格处理列表和布局。

这些都会有个通病就是无法动态处理宽度改变,以及低版本的安卓机没法控制缩放比例。

所以我们可以针对这些问题整理出一套完整的终端适配方案

设置rem

设置rem的意义在于得到一个与屏幕宽度相关的单位,本来vw是最合适的,但是因为兼容性的问题,只能使用rem来做。这样,让不同设备的rem显示一样的长度

vw是CSS3引入的单位,1vw = 1%窗口宽度

图片 5

上面的布局我们可以这样:

Sass

html{ font-size: 屏幕宽度 / 10; } .btn{ width:8.75rem; height:1.25rem; }

1
2
3
4
5
6
7
html{
    font-size: 屏幕宽度 / 10;
}
.btn{
    width:8.75rem;
    height:1.25rem;
}

这样,无论屏幕宽度是多少,.btn都是相对于屏幕的这么宽,做到了适配。

dpr = 设备像素 / CSS像素

2、设备独立像素

设备独立像素也称为密度无关像素,可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素(比如说CSS像素),然后由相关系统转换为物理像素。

基本概念

在进行介绍方案之前,首先得了解下面这些基本概念(术语):

视窗 viewport

简单的理解,viewport是严格等于浏览器的窗口。在桌面浏览器中,viewport就是浏览器窗口的宽度高度。但在移动端设备上就有点复杂。

移动端的viewport太窄,为了能更好为CSS布局服务,所以提供了两个viewport:虚拟的viewportvisualviewport和布局的viewportlayoutviewport。

而事实上viewport是一个很复杂的知识点,上面的简单描述可能无法帮助你更好的理解viewport,而你又想对此做更深的了解,可以阅读PPK写的相关教程。

物理像素(physical pixel)

物理像素又被称为设备像素,他是显示设备中一个最微小的物理部件。每个像素可以根据操作系统设置自己的颜色和亮度。正是这些设备像素的微小距离欺骗了我们肉眼看到的图像效果。

设备独立像素(density-independent pixel)

设备独立像素也称为密度无关像素,可以认为是计算机坐标系统中的一个点,这个点代表一个可以由程序使用的虚拟像素(比如说CSS像素),然后由相关系统转换为物理像素。

CSS像素

CSS像素是一个抽像的单位,主要使用在浏览器上,用来精确度量Web页面上的内容。一般情况之下,CSS像素称为与设备无关的像素(device-independent pixel),简称DIPs。

屏幕密度

屏幕密度是指一个设备表面上存在的像素数量,它通常以每英寸有多少像素来计算(PPI)。

设备像素比(device pixel ratio)

设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系。它的值可以按下面的公式计算得到:

  设备像素比 = 物理像素 / 设备独立像素

在JavaScript中,可以通过window.devicePixelRatio获取到当前设备的dpr。
而在CSS中,可以通过-webkit-device-pixel-ratio,-webkit-min-device-pixel-ratio和 -webkit-max-device-pixel-ratio进行媒体查询,对不同dpr的设备,做一些样式适配(这里只针对webkit内核的浏览器和webview)。

图片 6

视网膜屏幕

Meta标签

<meta>标签有很多种,而这里要着重说的是viewport的meta标签,其主要用来告诉浏览器如何规范的渲染Web页面,而你则需要告诉它视窗有多大。在开发移动端页面,我们需要设置<meta>标签如下:

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

CSS单位rem

简单的理解,rem就是相对于根元素<html>的font-size来做计算。而我们的方案中使用rem单位,是能轻易的根据<html>的font-size计算出元素的盒模型大小。而这个特色对我们来说是特别的有益处。

flexible解决方案

其实H5适配的方案有很多种,网上有关于这方面的教程也非常的多。不管哪种方法,都有其自己的优势和劣势。
flexible解决方案 这个库在手机淘宝已经使用了近一年,而且已达到了较为稳定的状态
除此之外,你不需要考虑如何对元素进行折算,可以根据对应的视觉稿,直接切入。

事实上他做了这几样事情:

  • 动态改写<meta>标签
  • <html>元素添加data-dpr属性,并且动态改写data-dpr的值
  • <html>元素添加font-size属性,并且动态改写font-size的值

flexible使用方法

只需要在Web页面的<head></head>中添加对应的flexible_css.js,flexible.js文件:

    <script src="build/flexible_css.debug.js"></script>
    <script src="build/flexible.debug.js"></script>

执行这个JS后,会在<code><html></code>元素上增加一个data-dpr属性,以及一个font-size样式。JS会根据不同的设备添加不同的data-dpr值,比如说2或者3,同时会给html加上对应的font-size的值,比如说75px。

除此之外,可以手动设置meta来控制dpr值,如:

    <meta name="flexible" content="initial-dpr=2" />

目前Flexible会将视觉稿分成100份,而每一份被称为一个单位a。同时1rem单位被认定为10a。
针对我们这份视觉稿可以计算出:

    1a   = 7.5px
    1rem = 75px     

那么我们这个示例的稿子就分成了10a,也就是整个宽度为10rem,<html>对应的font-size为75px

iphone 6的是实际宽度是375 * 667 ;

当前设备的dpr = 2 ;

所以设计图的宽度是 750 * 1134

在750Px设计图切出来的Icon比如说是85 * 85;

那我们给这个icon设置样式就是 width: 85/75;height:85/75

<b>CSSREM</b>

<p>CSSREM是一个CSS的px值转rem值的Sublime Text3自动完成插件。</p>
插件的效果:

图片 7

CSSREM

本人长期维护的前端公众号欢迎大家关注

图片 8

前端那些事

设置 viewport 缩放 和 data-dpr

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

Sass

.a{ font-size:12px; } [data-dpr="2"] .a{ font-size: 24px; } [data-dpr="3"] .a{ font-size: 36px; }

1
2
3
4
5
6
7
8
9
.a{
  font-size:12px;
}
[data-dpr="2"] .a{
  font-size: 24px;
}
[data-dpr="3"] .a{
  font-size: 36px;
}

而缩放是动态的,这样,不同设备下的px显示一样的长度

之前说过CSS像素和物理像素与缩放、dpr都有关系,这里说明:

在普通手机上,.a字体设置为12px;

在dpr是2的手机上,[data-dpr="2"] .a字体为24px,又因为页面缩放50%,字体为还是12px

可以通过JS得到: window.devicePixelRatio

3、设备像素比

设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系

设备像素比 = 物理像素 / 设备独立像素
以iphone6为例:
iphone6的设备宽和高为375pt * 667pt,可以理解为设备的独立像素,而其设备像素比为2.固有设备像素为750pt * 1334pt

通过:window.devicePixelRatio获得。

设备像素比是区别是否是高清屏的标准,dpr大于1时就为高清屏,一般情况下dpr为整数,但是android有些奇葩机型不为整数。

总结

坦白说,我不觉得第一种方案能就做『适配』方案,因为太笨了,只能做一些列表等简单排列的样式。

图片 9

但是一些复杂的活动页的布局,用它可能就力不从心了:

图片 10

这是我曾经做过的一个页面,『PK』要和左右两张图平行,而且下面的『不怒自威』、『义薄云天』和下面的战斗力位置都要固定,不能有差。如果用第一种方案,可能各个元素就要绝对定位,然后各种百分比来定位了。且不说计算麻烦,而且辛苦一番最后的结果尺寸是和设计图有出入的。

但是,第二种和第三种方案就绝对不会有这种情况,不会和设计图有差。再说第二种和第三种方案的区别,目前我唯一知道的区别就是第三种方案更加灵活,有两种单位可以使用,想让元素适配的时候就用rem,想让文字不缩放的时候就用px

如果你没有明白我以上讲的,可能是我太啰嗦了,这是我在团队内分享时做的PPT,应该没那么啰嗦了。当然你也可以看看下面的那些文章,不过最好的理解方式就是用这些方案做几个页面,到时候就明白了。

其他文章

  • 移动端高清、多屏适配方案
  • 从网易与淘宝的font-size思考前端设计稿与工作流
  • 百度方案
  • 移动端自适应方案 介绍了 flex 布局和rem方案

这两篇文章看着简单,尤其是上一篇讲视口,花费了将近一周的时间去翻阅资料。本来以为很简单的东西,才发现有那么多名堂。

2 赞 11 收藏 评论

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

4、css像素

在CSS、JS中使用的一个长度单位。单位px

注:在pc端1物理像素等于1px,但是移动端1物理像素不一定等于1px,1物理像素与px的关系与以下因素有关。(有些视口概念,可以把下面视口看完了再来看)

1、屏幕布局视口大小(下面会讲到)
2、屏幕的分辨率(物理像素)

对于一块屏幕,其物理像素是确定的。视觉视口尺寸是继承的布局视口的,而视觉视口里宽度即是css的px数。故在一块屏上物理像素与px的关系就是物理像素与布局视口的px数的关系。

比如iphone6,期物理像素为750,如果没有设置布局视口时,viewport为980px
此时:1物理像素长度等于980/750px = 1.3067px的长度
由于像素都是点阵的,故1物理像素相当于1.3067px * 1.3067px方格。
当在meta中设置了如下配置时
<meta name="viewport" content="width=device-width">
相当于把布局视口设置为设备的宽度(即上面讲到的设备独立像素), 对于iphone6就是375px。
此时1物理像素长度等于375/750px = 0.5px的长度,故1物理像素相当于0.5px * 0.5px的方格。

关于作者:risker

图片 11

2014年大学毕业,现在在北京某互联网公司从事前端开发的工作,近半年主要做移动web开发。微博粉丝太少,求粉。 个人主页 · 我的文章 · 7 ·   

图片 12

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

视口:

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

1、布局视口:

在html中一般在meta中的name为viewport字段就是控制的布局视口。布局视口一般都是浏览器厂商给的一个值。在手机互联网没有普及前,网络上绝大部分页面都是为电脑端浏览而做的,根本没有做移动端的适配。随着移动端的发展,在手机上看电脑端的页面已成为非常普及现象。而电脑端页面宽度较大,移动端宽度有限,要想看到整个网页,会有很长的滚动条,看起来非常麻烦。于是浏览器厂商为了让用户在小屏幕下网页也能够显示地很好,所以把布局视口设置的很大,一般在768px ~ 1024px 之间,最常用的宽度就是 980。这样用户就能看到绝大部分内容,并根据具体内容选择缩放。

故布局视口是看不见的,浏览器厂商设置的一个固定值,如980px,并将980px的内容缩放到手机屏内。

布局视口可以通过:

document.documentElement.clientWidth(clientHeight) // 布局视口的尺寸。
视觉视口

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

图片 13image.png

2、视觉视口:

浏览器可视区域的大小,即用户看到的网页的区域。(其宽度继承的布局视口宽度)

window.innerWidth(innerHeight)  // 视觉视口尺寸
布局适口

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

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

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

图片 14image.png

3、理想视口:

布局视口虽然解决了移动端查看pc端网页的问题,但是完全忽略了手机本身的尺寸。所以苹果引入了理想视口,它对设备来说是最理想的布局视口,用户不需要对页面进行缩放就能完美的显示整个页面。最简单的做法就是使布局视口宽度改成屏幕的宽度。

可以通过window.screen.width获取。

<meta name="viewport" content="width=device-width">

移动端到底怎么适配不同的屏幕呢?最简单的方法是设置如下视口:

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

当使用以上方案定义布局视口时,即布局视口等于理想视口(屏幕宽度),屏幕没有滚动条,不存在高清屏下,字体较小的问题。但是在不同屏幕上,其视觉宽度是不同的,不能简单的将所有的尺寸都设置为px,可能会出现滚动条。小尺寸的可以用px,大尺寸的只能用百分比和弹性布局。

理想视口

布局视口明显对用户是不友好的,完全忽略了手机本身的尺寸。所以苹果引入了理想视口的概念,把布局视窗调整到合适的状态,让页面有最好的表面效果。设置了理想视窗用户就不再需要对页面进行缩放,因为浏览器已经帮你把页面调整到最佳的显示状态了。而你要做的就是告诉浏览器,你要他这么做就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 /.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屏幕下就会出现位图像素点不够,从而导致图片模糊的情况。

用一张图来表示:

图片 15image.jpeg

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

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

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

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

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

viewport缩放

对于上面的设置,再不同的屏幕上,css像素对应的物理像素具数是不一致的。

在普通屏幕下,dpr=1时,

1个css像素长度对应1个物理像素长度,1个css像素对应1个物理像素。

而在Retina屏幕下,如果dpr=2,

1个css像素长度对应2个物理像素长度,1css像素对应4个物理像素。

此时如果css中写

border: 1px solid red; // 此时1px 对应的宽度是2物理像素的宽度。

而一般现在移动端设计稿都是基于iphone设计的,稿子一般为750px或640px,这正好是iphone6和iphone5的物理像素。在设计稿中,一般有些边框效果,这时边框的线宽为1px,对应的就是1物理像素。而对于iphone5和iphone6,当width=device-width时,css的1px显示出来的是2个物理像素,所以看起来线就比较粗。怎么解决呢?1px边框效果其实有很多hack方法,其中一种就是通过缩放viewport。

initial-scale是将布局视口进行缩放,initial-scale是相对于理想视口的,即initial-scale=1与width=device-width是一样的效果。initial-scale=0.5等效于width= 2倍的device-width,所以设置initial-scale和width都可以改变布局视口的大小。

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

对于iphone6当添加如上设置后,initial-scale=0.5,即将页面缩小2倍后等于屏幕宽度。

布局视口width:
width / 2 = 375px; width = 750px;

所以此时布局视口为750px,此时1px等于1物理像素。

方案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,") ;}

也可以使用border-image的方式

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

附注:

名称 方法 度量 说明 示例 移动端
屏幕尺寸 screen size screen.width / height 设备pixels 用户屏幕的完整大小,不重要 image.jpeg
浏览器尺寸 window size Window.innerWidth / innerheight css pixels[当zoom in放大的时候,数值变小] 包含滚动条尺寸的浏览器完整尺寸 image.png image.jpeg
滚动位移 scrolling offset window.pageXOffset / pageYOffset css pixels[当zoom in放大的时候,数值不变] 页面相对于窗口原点的水平/垂直位移 image.jpeg
视口viewpoint window.documentElement.clientWidth / cilentHeight css pixels 视窗内的css pixels image.jpeg image.jpeg
<html> 尺寸 window.documentElement.offsetWidth / offsetHeight css pixels html元素的尺寸 image.jpeg
Event.pageX / Y[使用较多] CSS pixels 从<html>原点到事件触发点的距离 image.jpeg
Event.clientX / Y CSS pixels 从viewport原点到事件触发点的距离 image.jpeg
Event.screenX / Y CSS pixels 从用户显示器窗口原点到事件触发点的距离 image.jpeg

参考文章:移动端Web页面适配方案移动前端开发之viewport的深入理解移动端适配方案 移动端适配方案

在简书上发布相关文章是对自己不断学习的激励;如有什么写得不对的地方,欢迎批评指正;给我点赞的都是小可爱 ~_~

适配方案:

上面讲了一些基础概念,下面讲具体适配。

对于ui设计师给的一张设计稿,怎么将其还原到页面上?对于不同手机屏幕,其dpr不同,屏幕尺寸也不同,考虑到各种情况,有很多适配方案,所以不同的适配方案,实现方法不同,处理复杂度也不同,还原程度也不同。

方案一

固定高度,宽度自适应。

这种方案是目前使用较多的方案,也是相对较简单的实现方案:

该方法使用了理想视口:

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

垂直方向使用固定的值,水平方向使用弹性布局,元素采用定值、百分比、flex布局等。这种方案相对简单,还原度也非常低。

方案二:

固定布局视口宽度,使用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">');
}

网易应用:

var win = window,
        width = 640,
        iw = win.innerWidth || width,
        ow = win.outerHeight || iw,
        sw = win.screen.width || iw,
        saw = win.screen.availWidth || iw,
        ih = win.innerHeight || width,
        oh = win.outerHeight || ih,
        ish = win.screen.height || ih,
        sah = win.screen.availHeight || ih,
        w = Math.min(iw, ow, sw, saw, ih, oh, ish, sah),
        ratio = w / width,
        dpr = win.devicePixelRatio;
        if (ratio = Math.min(ratio, dpr), 1 > ratio) {
            var ctt = ",initial-scale="   ratio   ",maximum-scale="   ratio,
                metas = document.getElementsByTagName("meta");ctt  = "";
            for (var i = 0, meta; i < metas.length; i  ) meta = metas[i], "viewport" == meta.name && (meta.content  = ctt)
        }

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

<meta name="viewport" content="width=640, minimum-scale = 0.5625, maximum-scale = 0.5625, target-densitydpi=device-dpi">

这种方式布局如荔枝FM的网页宽度始终为640px。缩放比例scale为:

var scale = window.screen.width / 640

设计稿为640px时,正好可以1:1以px来写样式。但是1px所对应的物理像素就不一定是1了。

(window.screen.width * dpr) / 640   // 1px对应的物理像素

图片 16

iphone5.png

图片 17

iphone6.png

方案三:

根据不同屏幕动态写入font-size,以rem作为宽度单位,固定布局视口。

如网易新闻:

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

以640px设计稿和750px的视觉稿,网易这样处理的:

var width = document.documentElement.clientWidth;   // 屏幕的布局视口宽度
var rem = width / 7.5;                              // 750px设计稿将布局视口分为7.5份
var rem = width / 6.4;                              // 640px设计稿将布局视口分为6.4份

这样不管是750px设计稿还是640px设计稿,1rem 等于设计稿上的100px。故px转换rem时:

rem = px * 0.01;

在750px设计稿上:

75px 对应 0.75rem, 距离占设计稿的10%;

在ipone6上:
width = document.documentElement.clientWidth = 375px;
rem = 375px / 7.5 = 50px;
0.75rem = 37.5px;   (37.5/375=10%;占屏幕10%)

在ipone5上:
width = document.documentElement.clientWidth = 320px;
rem = 320px / 7.5 = 42.667px;
0.75rem = 32px; (32/320=10%;占屏幕10%)

故对于设计稿上任何一个尺寸换成rem后,在任何屏下对应的尺寸占屏幕宽度的百分比相同。故这种布局可以百分比还原设计图。

图片 18

iphone5-2.png

图片 19

iphone6-2.png

方案四:

以rem作为宽度单位,动态写入viewport和font-size进行缩放。

根据设置的dpr设置font-size。如:

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

这种情况下,dpr = 1时,1rem = 50px;

dpr = 2时, 1rem = 100px;

当设计以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 = 1
if ( 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;

所以该方案,1rem在所有屏幕上对应的肉眼距离相同,故不同屏幕下,总的rem数不同,大屏下总的rem数大于小屏下,如iphone6下,总宽度为7.5rem,iphone5下,总宽度为6.4rem。故此方案不能百分比还原设计稿,故写样式时,对于大块元素应该用百分比,flex等布局,不能直接用rem。

关于这个方案的详细教程请参考这篇文章传送门

图片 20

iphone5-3.png

图片 21

iphone6-3.png

方案五:

根据不同屏幕动态写入font-size和viewport,以rem作为宽度单位

将屏幕分为固定的块数10:

var width = document.documentElement.clientWidth;   // 屏幕的布局视口宽度
var rem = width / 10;                               // 将布局视口分为10份

这样在任何屏幕下,总长度都为10rem。1rem对应的值也不固定,与屏幕的布局视口宽度有关。

对于动态生成viewport,他们原理差不多,根据dpr来设置缩放。看看淘宝的:

var devicePixelRatio = window.devicePixelRatio;
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
var dpr,scale;
if (isIPhone) {
  if (devicePixelRatio >=3) {
    dpr = 3;
  } else if (devicePixelRatio >=2) {
    dpr = 2;
  } else {
    dpr = 1;
  }
} else {
  dpr = 1;
}
scale = 1 / dpr;

淘宝只对iphone做了缩放处理,对于android所有dpr=1,scale=1即没有缩放处理。

此方案与方案三相似,只是做了viewport缩放,能百分比还原设计稿。

图片 22

iphone5-4.png

图片 23

iphone6-4.png

适配中要解决的问题 :

移动端适配最主要的是使在不同屏幕下不用缩放页面就能正常显示整个页面。以上方案都完成了这一需求。其次有几个需求:

1、解决高清屏下1px的问题,其实有很多hack方法,这里只讲了缩放视口。先将布局视口设置为高清屏的物理像素。这样css中1px就是1个物理像素,这样看到的线条才是真正的1px。但是此时视口宽度大于设备的宽度,就会出现滚动条。故对视口进行缩放,使视口宽度缩放到设备宽度。

淘宝团队在处理安卓端的缩放存在很多问题,所以dpr都做1处理,所以安卓端就没有解决1px的问题。

2、在大屏手机中一行看到的段落文字应该比小屏手机的多。

由于淘宝和网易新闻rem都是百分比,故如果用rem一行显示的文字个数应该是相同的。故对于段落文本不能用rem作为单位,应该用px处理,对于不同的dpr下设置不同的字体。

.selector { 
  color: red; 
  font-size: 14px; 
}

[data-dpr="2"] .selector { 
  font-size: 28px; // 14 * 2
} 

[data-dpr="3"] .selector { 
  font-size: 42px; // 14 * 3
}

对于方案四,不管什么情况下,1rem对应的视觉上的宽度都是一样的,而对应的大屏、小屏手机其视觉宽度当然不同,故字体设置为rem单位时,也能满足大屏手机一行显示的字体较多这个需求。

五种方案对比:

上面四种方案对设计稿还原程度是有差别的。

除了方案一和方案四以外,其他方案都是百分比还原设计稿,大屏下元素的尺寸就大。

方案一还原设计稿程度较低,这里不做说明。

方案二做了百分比适配,部分1px适配,没有字体适配。

方案三做了百分比适配,没有1px适配,有字体大小适配。

方案四没有做百分百适配,布局要用百分百和flex布局,做了1px的适配,并且对于段落文字直接可以用rem做单位,不需要做适配。

方案五做了百分比适配,有1px适配,有字体大小适配。

项目中遇到的问题:

在我们项目中方案四和方案五都用过。

方案五在使用中没有遇到什么问题,就是刚开始没有做字体适配都是用的rem,后面加入了字体适配,这种方案设计师相对轻松些,不用考虑在大小屏幕下的布局效果。

方案四时没有跟ui设计师沟通清楚,导致设计师在设计图上一行排了很多交互元素,在小屏下放不下去,又不能简单放百分比(元素里的文字放不下)。所以还是要做动态判断大小屏,做出相应适配。这个方案可能设计师需要考虑的多些,尽量减少一行内的交互元素,当一行交互元素多时要考虑小屏手机怎么适配。

其实对于1px的适配在苹果端很好,在android端各个厂商手机差别太大,适配有很多问题。这是为什么绝大多数方案里都放弃了android端1px适配。不过最近看到很多网站都用了densitydpi=device-dpi这个安卓的私有属性来兼容部分安卓机型,这个属性在新的webkit已经被移除了,使用它主要为了兼容低版本的android系统。

这里大漠老师针对flexible方案进行了改版,兼容了更多的android机型的1px效果。文章传送门

他给了个压缩版的方案,我看了下源码,把它写了一遍,不知道有没有问题,效果是一样的。

var dpr, scale, timer, rem;
var style = document.createElement('style');

dpr = window.devicePixelRatio || 1;
scale = 1 / dpr;

document.documentElement.setAttribute('data-dpr', dpr);
var metaEl = document.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'target-densitydpi=device-dpi, initial-scale='   scale   ', maximum-scale='   scale   ', minimum-scale='   scale   ', user-scalable=no');
document.documentElement.firstElementChild.appendChild(metaEl);
document.documentElement.firstElementChild.appendChild(style);
if (980 === document.documentElement.clientWidth) {
  metaEl.setAttribute('content', 'target-densitydpi=device-dpi,width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1');
}

function refreshRem () {
  var c = '}';
  var width = document.documentElement.clientWidth;
  var isPhone = window.navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i);
  if (!isPhone && width > 1024) {
    width = 640;
    c = 'max-width:'   width   'px;margin-right:auto!important;margin-left:auto!important;}';
  }
  window.rem = rem = width / 16;
  style.innerHTML = 'html{font-size:'   rem   'px!important;}body{font-size:'   parseInt(12 * (width / 320))   'px;'   c;;
}

refreshRem();

window.addEventListener('resize', function () {
  clearTimeout(timer);
  timer = setTimeout(refreshRem, 300);
}, false);

window.addEventListener('pageshow', function (e) {
  if (e.persisted) {
    clearTimeout(timer);
    timer = setTimeout(refreshRem, 300);
  }
}, false);

这些方案只是针对绝大部分机型,项目中可能有些特殊机型有特殊问题,需要特殊对待。比如在这篇文章中作者使用flexible在小米max和荣耀8中有问题,需要特殊hack。传送门,我没有这种手机,也没有对此做验证。

对于上面的五种方案,方案五看似是适配最好的,但是当项目中引入第三方插件时可能要一一适配,比如:引入一个富文本,里面设置字体大小的一般都是px,你需要将其一一转换成rem。而对于方案二,可以直接用px做单位来百分百还原设计稿,引入的插件时也不用适配。所以说,具体项目中用哪个方案,其实不光是前端的选择,还要跟设计师讨论下,满足设计需求,选择最适合项目的方案。

以上是个人对于移动端适配的一些理解,如有不对欢迎指正。

参考文章:

移动前端开发之viewport的深入理解

再谈Retina下1px的解决方案

使用Flexible实现手淘H5页面的终端适配

移动端适配方案(上)

viewports剖析

本文由pc28.am发布于前端技术,转载请注明出处:运动端适配方案,多终端的适配实施方案

上一篇:App的成才历程,笔者所理解的前端 下一篇:没有了
猜你喜欢
热门排行
精彩图文
  • 运动端适配方案,多终端的适配实施方案
    运动端适配方案,多终端的适配实施方案
    移动端适配方案(下) 2017/01/25 · CSS ·移动端 本文作者: 伯乐在线 -risker。未经作者许可,禁止转载! 欢迎加入伯乐在线 专栏作者。 上一篇介绍了像素和视
  • 内部存储器分析工具简要介绍,内部存款和储蓄
    内部存储器分析工具简要介绍,内部存款和储蓄
    4类 JavaScript 内存泄漏及如何避免 2016/05/26 · JavaScript· 1 评论 ·内存泄漏 本文由 伯乐在线 -涂鸦码龙翻译。未经许可,禁止转载! 英文出处:SebastiánPeyro
  • HTML也可以静态编译,损害了复用性
    HTML也可以静态编译,损害了复用性
    React.Component 损害了复用性? 2016/09/07 · 底蕴技能 ·binding.scala,data-binding,React,scala.js 本文笔者: 伯乐在线 -ThoughtWorks。未经笔者许可,防止转发! 接待插足
  • 品质的法门
    品质的法门
    9 种改革 AngularJS 品质的艺术 2017/07/20 · JavaScript· AngularJS 初藳出处: JustinSpencer   译文出处:oschina    AngularJS 是当下利用非常遍布的 web app应用框架,随
  • 高质量滚动,实例解析防抖动和节流阀
    高质量滚动,实例解析防抖动和节流阀
    实例解析防抖动和节流阀 2016/04/26 · JavaScript· DOM 本文由 伯乐在线 -涂鸦码龙翻译。未经许可,幸免转发! 立陶宛共和国(Republic of Lithuania卡塔尔语出处: