Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

px、dp、dpr、ppi、viewport 相关概念 #8

Open
muwoo opened this issue Jan 23, 2018 · 3 comments
Open

px、dp、dpr、ppi、viewport 相关概念 #8

muwoo opened this issue Jan 23, 2018 · 3 comments

Comments

@muwoo
Copy link
Owner

muwoo commented Jan 23, 2018

最近一直在看一些手淘移动端适配rem之类的技术方案,在研究这些技术方案之前,首先需要掌握一些基本的单位概念,所以在网上也搜了一些资料。虽然全部看下来还是存在一些疑惑的地方,在此做一下记录。

有趣的问题

人物:前端实习生「阿树」与 切图工程师「玉凤」
事件:设计师出设计稿,前端实现页面

玉凤:树,设计稿发给你啦,差那么点像素,就叼死你┏(  ̄へ ̄)=☞
阿树:(>_<)没问题啦~
阿树:哇靠,为啥你给的设计稿是750px宽 ,iPhone 6不是375px宽吗???
玉凤:A pixel is not a pixel is not a pixel, you know ?
阿树:(#‵′),I know Google。。。

为什么会出现以上的情况,难道他们当中一位出错了,摆了这样的乌龙?
事实上,他们都是对的,只是谈的不是同一个「像素」。

1. dp (设备像素)

1.1 概念

设备像素设是物理概念,指的是设备中使用的物理像素,顾名思义,显示屏是由一个个物理像素点组成的,通过控制每个像素点的颜色,使屏幕显示出不同的图像,屏幕从工厂出来那天起,它上面的物理像素点就固定不变了,单位pt。

1.2 相关说明

设备像素其实就是一个固定的尺寸,1pt = 1/72(inch),inch及英寸,而1英寸等于2.54厘米。
不同的设备,其图像基本单位是不同的,比如显示器的点距,可以认为是显示器的物理像素。现在的液晶显示器的点距一般在0.25mm到0.29mm之间。而打印机的墨点,也可以认为是打印机的物理像素,300DPI就是0.085mm,600DPI就是0.042mm。

2. px (CSS pixels)

CSS像素是Web编程的概念,指的是CSS样式代码中使用的逻辑像素。在CSS规范中,长度单位可以分为两类,绝对(absolute)单位以及相对(relative)单位。px是一个相对单位,相对的是设备像素(device pixel)。

比如iPhone 6使用的是Retina视网膜屏幕,使用2px x 2px的 device pixel 代表 1px x 1px 的 css pixel,所以设备像素数为750 x 1334px,而CSS逻辑像素数为375 x 667px。
image

3. dpr(Device Pixel Ratio) 设备像素比

设备像素比表示1个CSS像素(宽度)等于几个物理像素(宽度):

DPR = 物理像素数 / 逻辑像素数

比如dpr=2时,1个CSS像素宽度等于2个物理像素宽度。1css像素由2 * 2个物理像素点组成,见上面对CSS像素的解释。DPR不是单位,而是一个属性名,比如在浏览器中通过window.devicePixelRatio获取屏幕的DPR。

4. ppi (pixel per inch)每英寸的像素数,像素密度。

image

5. viewport 视窗

在桌面浏览器中,viewport就是浏览器窗口的宽度高度。
但移动设备的屏幕比桌面屏幕要小得多,为了要让网页在小尺寸的屏幕上显示正确,就需要对viewport做些处理。需要把viewport分成两部分:visual viewport和layout viewport。George Cummins在Stack Overflow上对这两个概念做了分析。大致意思如下:

把layout viewport想像成为一张大图。现在用一个比较小的框,通过它来看这张大图。在框内看到的部分就是visual viewport。框中的度量单位是CSS像素。可以把这个框靠近一些(放大看局部)或靠远一些(缩小看整体)。也可以改变框的方向,但是大图layout viewport的大小和形状永远不会变。

5.1 visual viewport

visual viewport是页面当前显示在屏幕上的部分。用户可以通过滚动来改变他所看到的页面的部分,或者通过缩放来改变visual viewport的大小。
image

5.2 layout viewport

layout viewport就是页面原来的大小。
image
但是我们用在手机用浏览器打开PC的网页的时候,会看到网页被浏览器自动缩小了,变的太小会导致无法浏览内容。

5.3 idea viewport

布局视口的默认宽度并不是一个理想的宽度,大家从上面的图就可以看出来了,所以苹果公司就引进了理想窗口这个概念:

它是对设备来说最理想的布局视口尺寸。显示在理想视口中的网站拥有最理想的浏览和阅读的宽度,用户刚进入页面时不再需要缩放。

注意:

  • 理想窗口的尺寸是由浏览器厂商决定的,同一设备可以有不同的尺寸
  • 不同设备的相同浏览器理想窗口也会不同,比如手机和平板
    而且会随着设备转向改变
  • 虽然有那么多不同尺寸的理想视口,但是平时开发我们只要告诉浏览器使用它的理想视口(也就是width=device-width或者initial-scale=1.0),就没问题了。

5.4 viewport meta标签

为了不让浏览器自动缩小,引入了viewport元标签。通过这个元标签控制layout viewport的宽度。

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

上面这行代码就是告诉浏览器,布局视口的宽度应该与理想视口的宽度一致。

  1. width:设置 layout viewport 的宽。
  2. initial-scale:初始缩放比例,也即是当页面第一次 load 的时候缩放比例,上面是变成设备的宽度,也就是layout viewport= DP或PT。
  3. maximum-scale:允许用户缩放到的最大比例。
  4. minimum-scale:允许用户缩放到的最小比例。
  5. user-scalable:用户是否可以手动缩放。

image

注意下图片中的红色框,第二张图片内容超了出来,应该是给文字设置了宽度,并且这个宽度大于layout viewport导致了出来。但其实在Android和IOS中会有不一样的表现。在文章《A tale of two viewports》中指出:

  1. 通过 document.documentElement.clientWidth 获取 layout viewport 的宽度
  2. 通过 window.innerWidth 获取 visual viewport 的宽度

移动端 1px 像素问题

一直以来我们实现边框的方法都是设置 border: 1px solid #ccc ,但是在 retina 屏上因为设备像素比(dpr)的不同,边框在移动设备上的表现也不相同: 1px 可能会被渲染成 2px, 3px....也就是说逻辑像素1px会被用不同大小的物理像素来表示。(这里只介绍几种常用的方式,更多可以自行Google)

rem + viewport

按照上面这种表述,首先想到的最快解决这种问题的肯定是根据dpr不同来进行缩放就好了,这种方式也就是常说的 rem + viewpor。关于rem的介绍可以参考这里。核心的实现如下:

<script>  
            var viewport = document.querySelector("meta[name=viewport]");  
            //下面是根据设备像素设置viewport  
            if (window.devicePixelRatio == 1) {  
                viewport.setAttribute('content', 'width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no');  
            }  
            if (window.devicePixelRatio == 2) {  
                viewport.setAttribute('content', 'width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no');  
            }  
            if (window.devicePixelRatio == 3) {  
                viewport.setAttribute('content', 'width=device-width,initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no');  
            }  
            var docEl = document.documentElement;  
            var fontsize = 10 * (docEl.clientWidth / 320) + 'px';  
            docEl.style.fontSize = fontsize;   
</script>  

大概意思就是设置网页根字体 font-size 为 37.5 * dpr,这样根据 rem 产出的网页就会被放大 dpr 倍,此时css里面写的还是1px,然后再通过initial-scale= 1 / dpr来对网页进行缩小dpr倍。这样 1px 就会显示成 1 / dprpx。

border-image 实现

这篇文章是腾讯github上的解决方案border-image来实现的。缺点是,你需要制作图片,圆角的时候会出现模糊。

.border-image-1px {
    border-width: 1px 0px;
    -webkit-border-image: url("border.png") 2 0 stretch;
    border-image: url("border.png") 2 0 stretch;
}

border.png也可以用 base64 图片替代。

background-image 渐变实现

除啦用图片,难道纯粹的css就不能实现吗?我的确不想使用图片,感觉制作起来很麻烦,其实百度糯米团首页就是这么做的但是这种方法有个缺点,就是不能实现圆角

.border {
      background-image:linear-gradient(180deg, red, red 50%, transparent 50%),
      linear-gradient(270deg, red, red 50%, transparent 50%),
      linear-gradient(0deg, red, red 50%, transparent 50%),
      linear-gradient(90deg, red, red 50%, transparent 50%);
      background-size: 100% 1px,1px 100% ,100% 1px, 1px 100%;
      background-repeat: no-repeat;
      background-position: top, right top,  bottom, left top;
      padding: 10px;
  }

box-shadow 实现

利用阴影我们也可以实现,那么我们来看看阴影,优点是圆角不是问题,缺点是颜色不好控制。

div{
    -webkit-box-shadow:0 1px 1px -1px rgba(0, 0, 0, 0.5);
}

伪类 + transform 实现

原理是把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位。
单条border样式设置(其他的类似):

.scale-1px{
  position: relative;
  border:none;
}
.scale-1px:after{
  content: '';
  position: absolute;
  bottom: 0;
  background: #000;
  width: 100%;
  height: 1px;
  -webkit-transform: scaleY(0.5);
  transform: scaleY(0.5);
  -webkit-transform-origin: 0 0;
  transform-origin: 0 0;
}

优点可以实现圆角,京东就是这么实现的。缺点是按钮添加active比较麻烦,对于已经使用伪类的元素(例如clearfix),可能需要多层嵌套。

@muwoo muwoo changed the title px、dp、dip、dpr、ppi、viewport 相关概念 px、dp、dpr、ppi、viewport 相关概念 Jan 29, 2018
@lisalin880505
Copy link

理想视窗的观点应该不对吧,这个应该是浏览器自己决定的,并不是计算公式出来的。另外,设置initial-scale和设置width的功能接近,都是用来设置layout viewport的宽度的,为什么要先产生visual viewport?我认为visual viewport是浏览器默认为了显示全部内容,所以第一次加载页面的时候将visual viewport设置成layout viewport的宽度,当你缩放页面后再刷新,它会保留上次visual viewport的宽度,和meta里面的值并没有什么关系。

@muwoo
Copy link
Owner Author

muwoo commented Oct 16, 2018

@lisalin880505 感谢,已纠正

@quirkybird
Copy link

所以开头的问题是一个是逻辑像素一个是物理像素对吗?还有一个疑问是使用安卓模拟器时,我调整不同的DPR后,页面没有任何改变

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants