Gu's Blog

对于Flexible和移动端适配的一些理解

Flexible是什么

在Mobile时代,h5页面的开发要面临很多问题。比如设备尺寸的大小,我们要去考虑适配大小问题;高分辨率屏的出现,我们要去考虑图的清晰度的问题;同时呢,视觉稿和开发应该怎么的一套规范问题。等等,在这样的背景之下呢,手淘提出了一套适配多终端的方案–Flexible方案

在了解这个方案之前,我们先来抛开方案,看看本质。


viewport

在移动端,设备能展示的内容往往要比桌面端浏览器少的多的多,如果按照桌面浏览器的布局直接在移动端展示,所有的东西都会变的特别特别小,根本不利于用户查看。因此, 引入了三个viewport的概念: layout viewport、visual viewport、ideal viewport。

转载一个来自@承风同学的译文中的解释,原文链接在最下方:

想象下layoutviewport是一张大的不能改变大小和角度的图片。现在你有个更小的框来观看这张大图片,这个框被不透明的材料包围,因而你只能看到大图片的一部分。你通过这个框子看到的大图片的部分被称为虚拟viewport(visual viewport)。你能拿着这个框站得离大图片远点(用户的缩小页面功能),以一次性看到这个大图片。或者你能站得近点(用户的放大页面功能)以看到一部分。你能改变这个框子的方向,但这张大图片的大小和形状都不会改变。

  • 布局viewport
    布局viewport就是各大厂商,为了手机端适配搞出来的概念。因为手机屏幕小啊,不能像pc端那样,视图的大小等于屏幕的大小,那么所有的内容都被挤成一团了。于是,他们默默地都给设备搞了一个layoutviewport的概念。大部分手机呢,比如iphone基本默认都是980px。具体参数可以自行查下资料。
    html的宽度呢,继承于layoutviewport,所以我们可以用document.documentElement.clientWidth来读取layoutviewport的宽度。
  • 虚拟viewport
    就是你眼睛可以看到的屏幕的大小。具体大小呢,随设备变化而变化。
    网上很多资料都是说window.innerHtml可以查看虚拟viewport的大小。但是实际我查看了chrome和safari的表现,window.innerHtml基本是和document.documentElement.clientWidth保持一致的。有一种说法是,现在浏览器默认让虚拟viewport等同于布局viewport。
    所以现在,window.innerHtml能不能代表虚拟viewport的值,需要打一个小小的问号。
    或者说,在现在基本手机端必设置viewport的meta标签的情况下,虚拟viewport的这个概念变得更加遥远了。
  • 理想viewport
    在这种混乱的情况之下呢,提出了第三种概念,理想视图。
    我们不想通过手动横向滚动或者缩放去看到所有的内容,就想一眼就看到所有的内容宽度。理想viewport就是设备的宽度, 我们可以用device-width来表示。

我们为了实现理想viewport,需要做什么呢。
利用meta标签对viewport控制。

meta标签怎么控制viewport

这是我们最常见的设置:

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

以上呢,使layoutviewport的宽度等于设备宽度, 达到了我们所谓的理想视图。可见呢,meta标签的width控制的是layoutviewport。

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

这种情况呢,也能实现理想视图效果。因为initial-scale是相对理想视图进行缩放的。

当width和initial-scale冲突的时候呢,浏览器会自动选一个宽度较大的。

当然meta标签还有别的属性值,具体就不赘述了。

scale改变了什么

当前缩放值 = 理想视图 / 布局视图
假设设置了<meta name="viewport" content="initial-scale=0.5">这种情况呢,在iphone 6s的屏幕下(理想视图375px),布局视图就是750px。
消化一下,缩小了0.5倍,原来1px所占的位置宽度w,现在0.5w的就能代表1px了,同样的设备大小呢,能展示的就更多了。


到底什么是Retina屏

大家一直在讲,哇Retina屏的清晰度高,跟一般的屏幕就是不一样。那到底什么是Retina呢。这里我就概括一下大漠大佬的解释:

“Retina”一词,原意是“视网膜”的意思,指显示屏的分辨率极高,使得肉眼无法分辨单个像素。这是一种新的屏幕的显示技术,当屏幕像素密度超过300ppi的时候时,人的肉眼就无法区分出单独的像素。苹果的销售术语”Retina”意思呢,称为双密度显示,显示设备清晰度已达到人视网膜可分辨像素的极限。

别急,我们再来细细讲一讲这些熟悉又陌生的名词。

首先,在这小段里面,所有的默认情况,指的都是理想视图(layoutviewport等于设备宽度)。

一些总也理不清的概念

物理像素

设备中最小的物理部件,也称作设备像素。
我们把屏幕颗粒化,由一块块砖(方砖)组成。方砖的大小,颜色不重要,都是由所处的设备决定的(设备的大小,设备能达到的分辨率等等)。在某一些因素之下, 我就决定了我这个设备的最小单位就是这个砖。啪!敲定了!不能更改。

设备独立像素(dp)

可以简单的看做设备单位坐标中的一个点。理想视图下,我们所写的css的1px对应的就是一个设备独立像素
那么,所谓的Retina屏,比如iphone 6s的2倍屏。什么意思呢?一个设备独立像素里面,由四个物理像素组成。
那么,我们所表现的1px(css)是不是就更具象化了?在一倍屏里面,1px(css)由一个物理像素(一倍屏:1个物理像素===1个设备独立像素)表现,而6s中的1px(css)由四个物理像素表现,细节更多了,我们人眼自然会认为屏幕清晰度更高了呀(但是图有时候会觉得糊,是因为图片的质量没达到)。

献上一张来自w3cplus的经典的图:
Retina web

css像素

css像素应该是我们前端开发最熟悉的。是一个虚拟抽象的单位。
ps打开的视觉稿,有n px * n px,我们浏览器开发者工具打开,读取样式的时候也会是px。但是呢,在物理单位上,两者的1px并不一定的大小的,这要看设备本身的一个设备独立像素(dp)占多大了。
有这样的一个虚拟单位,为我们提供了一种标准,我们可以将样式化为一种单位去衡量。

屏幕密度

设备的上存在的像素数量。一般是以每英寸多少像素来计算(PPI)。当然这个像素,指的是物理像素。英寸是个长度单位,1英寸=2.54厘米。
那么这个屏幕密度呢,就是指对角线为一英寸的正方形中所拥有的像素点。

设备像素比

设备像素比(dpr),是一个比较重要的概念。也就是我们口上经常所说的,几倍屏。
设备像素比 = 物理像素 / 设备独立像素
我们可以通过window.devicePixelRatio来获取当前屏幕的dpr。

卖手机的时候那些参数到底是啥

手机售卖的时候,官网总是有一系列参数,那么这些参数到底都有啥?

  • 尺寸
    英寸为单位,和屏幕密度的英寸一样,也是对角线的长度。比如iphone 6s的 4.7英寸,也就是6s的屏幕对角线长4.7英寸。
  • 分辨率
    还是拿6s举例,1334×750像素。意思呢,就是6s的屏幕上,有1334x750个像素点(物理像素)。
  • PPI
    上面已经描述过了, 屏幕密度,一般用来衡量屏幕的清晰度。

视觉的那些XX倍图

说完了设备屏幕的不同点,那么再来讲讲这些XX倍图。
上文已经献出了那张经典的来自大漠老师的图,就解释了一般图在高清屏上为什么会觉得模糊。那么为了适应这些高清屏呢,视觉大大经常就会给我们开发1倍图,2倍图, 3倍图等等。
打个比方,还是我们的iphone 6s。前面我已经说过了,理想视图下,6s的正常宽度就是375px。视觉大大给两倍图呢,就会给一个放大版的,750px宽度的。那么这种时候,我们会怎么处理呢。
利用background-size属性,设置为100%。那么在一个宽为375px的容器里面,就能等比挤下一个宽度为750px的图片。一个图片虽然大小被压缩了,但是细节不会被压缩哦。所以这张图还是由750个像素组成(可以理解为css像素)。
前面解释过在6s的手机屏幕上呢,正常情况下(也就是一倍图的情况)一个css像素对应4个物理像素。那么用了两倍图之后,图片上的1px只对应了一个物理像素,那么就不会给用户造成模糊的感觉了。

做了一张图,来个简单的6s版的示意:

其实概括一下,我们清晰的目标一定意义上就是:1px(css像素)对应一个物理像素


html的font-size和rem单位

rem是font size of the root element, 意思是根据网易根元素的font-size来设置大小的。
假设html的font-size是16px,那么我们设置一个div的宽度为10Rem,就是160px的宽度。
当整个页面都以rem为参照物的时候,所有的元素都统一了一套标准,依赖于根元素的font-size。
那么,我们是否可以在不同屏幕给根元素设置不同的font-size大小,来达到适配的目的。


总结

Fleible做的东西

Fleible的方案呢,其实就是做了以下几件事:

  • 给html标签设置data-dpr属性
    在项目中,我们可以根据这个data-dpr做一些文章
  • 改写meta标签,设置scale。
    设置scale的标准呢,基本也是本着一个目的: 让1px对应一个物理像素(设备像素)。
    比如iphone 6s。scale就是0.5, layoutview的大小呢就变成了750px。而6s的dp是375,dpr又是2,所以物理像素也是750.
  • 给html标签设置font-size, 定一个页面的单位基数。
    把页面的大小的十分之一(好算),作为一个单位基准。那么6s的html的font-size就是75px。

关于字体

由于现在屏幕流行的越来越大, 字体随着屏幕宽度的放大而放大,槽点越发明显了。这种方案,不管在多大的屏幕上,展示的东西都是一样多的,每个手机上都因为宽这条边上的物理像素,进行大小的等比缩放。
有些时候呢在大屏幕上,会觉得元素过于大,希望适当缩小一点元素,可以展示更多的东西;但在小屏幕手机上呢,又得维持原样。另一方面在大屏幕手机上,这种方案,会导致字体特别大,也会让用户感觉不舒适。
业界呢也有了一些解决方案,利用html上的data-dpr标签,分别根据屏幕的dp去设置字体大小(px),字体不走等比缩放的方案;
或者直接放弃这种方案,秉持元素都往中间靠的原则,用留白去解决大屏幕手机多出的空间。

强烈致谢!!!参考链接如下:
使用Flexible实现手淘H5页面的终端适配
走向视网膜(Retina)的Web时代
viewports剖析
移动前端开发之viewport的深入理解