Open shizhihuaxu opened 3 years ago
[TOC]
分辨率:是在这块大小的屏幕上拥有的像素数。显示器可显示的像素越多,画面就越精细,同样的屏幕区域内能显示的信息也越多。物理像素所指的就是分辨率。
每英寸像素数 ppi = (横向 pixel的平方 + 纵向 pixel的平方)开平方 / 屏幕对角线尺寸
ppi 越大越细腻,清晰
独立于设备的用于逻辑上衡量长度的单位,由底层系统的程序使用,会由相关系统转换为物理像素。
举个例子,iPhone 3GS 和 iPhone 4/4s 的尺寸都是 3.5 寸,但 iPhone 3GS 的分辨率是 320x480,iPhone 4/4s 的分辨率是 640x960,这也就是意味着同样长度的屏幕,iPhone 3GS 有 320 个物理像素,iPhone 4/4s 有 640 个物理像素。
如果我们按照真实的物理像素进行布局,比如说我们按照 320 物理像素进行布局,到了 640 物理像素的手机上就会有一半的空白,为了避免这种问题,就产生了虚拟像素单位。我们统一 iPhone 3GS 和 iPhone 4/4s 都是 320 个虚拟像素,只是在 iPhone 3GS 上,最终 1 个虚拟像素换算成 1 个物理像素,在 iphone 4s 中,1 个虚拟像素最终换算成 2 个物理像素。至于 1 个虚拟像素被换算成几个物理像素,这个数值我们称之为设备像素比。
即物理像素和设备独立像素的比值。
dpr = 分辨率 / 设备独立像素
dpr 表示了 需要多少个物理像素表示一个css 像素,假设dpr =1 ,那么在 1css 像素就用一个物理像素表示,
如果dpr =2 , 1css 像素就需要 2 个物理像素来显示 (2 * 2 横向和竖向),
在没有dpr 和 scale 的情况下那么对于同一块屏幕来说,ppi 是一定的,也就是每英寸上的物理像素数是一定的,反过来想,每个物理像素数所占的屏幕尺寸也是一定的。
再从dpr 来讲, dpr =1时, 1css 像素 =1 物理像素 -> 此时1物理像素占屏幕尺寸的大小
现在增大 dpr = 2 ,也就是 1css 能表示更多的物理像素,
1css = 2 物理像素 -> 2物理像素所占用的屏幕尺寸(会变大,所以字会看起来大一些)
1280 * 1.5 = 1920
假设都是1920* 1080 的分辨率,那么手机的ppi 会远大于 pc 端的 ppi ,不同尺寸pc ppi 也不同,那么浏览器会为移动端和小尺寸屏幕 pc 设置dpr 。ppi 越大,表示 每英寸拥有的物理像素数越多,反过来想,1 物理像素所占的尺寸就会越小,如果 1css 还表示1物理像素数,那么字体会变得非常小,所以浏览器设置了 dpr 后, dpr越大,1css 表示的物理像素数越多,占的屏幕尺寸会大些,才能看起来正常,例如在移动端 dpr 会比pc端大。
web 开发中使用 1css 像素 = 1 设备独立像素
在非高清屏幕以及未缩放浏览器的情况下,一个CSS像素等于一个物理像素(分辨率);
而在搭载了苹果公司的视网膜显示屏等高清屏幕(PPI特别高,一个屏幕上的物理像素点非常多)的设备上,如果一个CSS像素仍等于一个物理像素,那么网页上的各种元素就变得非常小,用户很难看清,因此高PPI的设备中,一个CSS像素通常等于两个甚至三个物理像素(浏览器自动设定,不同浏览器设定的会不一样)
视口在CSS里的定义是html元素的包含块,称为初始包含块。
桌面浏览器中,浏览器窗口就是约束你的CSS布局视口(又称初始包含块)。它是所有CSS百分比宽度推算的根源,它的作用是CSS布局限制了一个最大宽度,视口的宽度和浏览器窗口宽度一致。
但是在移动端,情况就很复杂了。
一个没有为移动端做优化的网页,会尽可能缩小网页让用户看到所有东西。
浏览器厂商为了让用户在小屏幕下网页也能够显示地很好,所以把视口宽度设置地很大,一般在 768px ~ 1024px 之间,最常见的宽度是 980px。
meta viewport 是为了解决移动端的问题才出现的
width = device-width 是设置布局视口的宽度,被缩放在屏幕上
媒体查询中的 min-width 也是指的布局视口的宽度
布局视口明显对用户是不友好的,完全忽略了手机本身的尺寸。所以苹果引入了理想视口的概念,它是对设备来说最理想的布局视口尺寸。理想视口中的网页用户最理想的宽度,用户进入页面的时候不需要缩放。
现在讨论所谓的『最理想的宽度』到底是多少?其实,如果我们把布局视口的宽度改成屏幕的宽度不就不用缩放了么。可以这样设置告诉浏览器使用它的理想视口:
<meta name="viewport" content="width=device-width">
布局视口,默认的宽度是document.documentElement.clientWidth = 980px 不同浏览器默认值不同,浏览器厂商决定的
视觉视口,浏览器窗口的大小 window.innerWidth
桌面上 布局视口等于视觉视口
没超过默认值时 clientWidth = innerWidth (有垂直滚动条会包含垂直滚动条的宽度)
理想视口: 布局视口 = 视觉视口 width = device-width(就是设备独立像素 ) = screen.width/height返回理想视口的尺寸,在内置浏览器和下载的浏览器有严重的兼容性问题,但是下载的浏览器返回的都是理想视口尺寸 px单位
缩放值 scale = 理想视口 (布局视口等于屏幕宽度的视口,但是这个屏幕宽度还得转换成css px 的单位,所以是分辨率处以dpr)/ 视觉视口
visual viewport宽度 = ideal viewport宽度 / 当前缩放值
scale 的问题:initial-scale 只能用在移动端, 实际屏幕宽度(width=device.scale=1时的宽度)/scale = client-width 视口
宽度最理想,意味着用户无需缩放页面,因此很多手机浏览器(不含safari)在这样的设置下,不再具有300ms的click时延。该时延的本意就是用来判断用户是要click还是双击缩放页面。
不同的设备有不同的理想宽度(同一设备的不同浏览器也有可能拥有不同的理想宽度,尽管这种现象极为极为罕见),且随着设备的横放竖放也会有所改变,因此通常不指定特定的尺寸,而是使用device-width进行自适应。宽度为理想宽度的布局视口叫做“理想视口”
当设备的横放、竖放方式被改变时,最佳DPR不变,布局视口宽度从"手机宽度/最佳DPR"变为"手机长度/最佳DPR",且因布局视口宽度有变,浏览器会进行一次重排。不过iOS上 的Safari在旋转时不会改变布局视口的宽度(因此DPR提高了),除非在meta指令里设置了initial-scale=1
为什么 视觉视口不是浏览器窗口的大小啊,内容设置了一个比较宽的宽度后,成了内容的宽度
让布局视口的值通过缩放(scale越小)变大,变得越大,放的视觉视口的内容不就越多了吗
initial-scale 呢影响的是什么 (还有移动端为了显示完全,默认会缩放,视觉视口)
而css 像素是一个逻辑值,它与物理像素的比例1关系受 浏览器dpr 和 scale (分辨率)后的影响。
一个dom元素的css在数值上大小不变,而设备的像素大小是物理值,也不变,所以也符合之前提到的。缩放实际改变的是一个css像素容纳的设备像素的多少。相当和dpr 的作用一样,但是dpr 没变
用户手动缩放不会影响布局视口的尺寸
开发者缩放设置scale 视觉视口副作用,会影响到布局视口的尺寸,dpr 不会变
实际上设置了width = device-width 就相当于设置了intial-scale =1,反之亦然,单独设置scale 或 width 会有兼容性问题,所以一般会同时设置,设置这个的作用是为了在移动设备上按屏幕宽度显示
(为什么实际测试的时候 设置scale 后clientWidth(布局视口)和 innerWidth(视觉视口)都被缩放了(开发者设置scale的副作用),但是如果内容宽度超过了这个宽度,innerWidth 会是文档的实际宽度+滚动条宽度)
本身媒体查询的min-width 是指布局视口的宽度
然后使用媒体查询
可以显示完全,不需要拖动滚动条来查看。
在不同尺寸的手机设备上,页面“相对性的达到合理的展示(自适应)”或者“保持统一效果的等比缩放(看起来差不多)”。
ui的设计稿只有一份,例如750px,如何针对这一份设计稿,让其在不同屏幕宽度下显示看起来差不多。
dpr 和 scale
字体、宽高、间距、图片。
图片自适应:利用 css 将图像限定在元素内( img 图片使用 [max-]width: 100% ,背景图像使用 background-size ),布局只针对元素进行。
元素使用固定的高度,宽度自适应(兼容性较好,但320px 过于窄小,不利于页面的设计;只能设计横向拉伸的元素布局,存在很多局限性)
viewport width 设置为 device-width,以较小宽度(如 320px)的视觉稿作为参照进行布局。
垂直方向的高度和间距使用定值,水平方向混合使用定值和百分比或者利用弹性布局,可以使用max-width 限定最大宽度,最终达到“当手机屏幕变化时,横向拉伸或者填充空白的效果”。
图像元素根据容器情况,使用定值或者 background-size 缩放。
元素使用固定的宽度,然后使用viewport 对页面及进行缩放
视觉稿、页面宽度、viewport width 使用统一宽度(主流分辨率、物理像素),利用浏览器自身缩放完成适配。
使用 rem 布局
依照某特定宽度设定 rem 值(即 html 的 font-size),页面任何需要弹性适配的元素,尺寸均换算为 rem 进行布局;当页面渲染时,根据页面有效宽度进行计算,调整 rem 的大小,动态缩放以达到适配的效果。利用该方案,还可以根据 devicePixelRatio 设定 initial-scale 来放大 viewport,使页面按照物理像素渲染,提升清晰度。
1. 750px设计稿 scale = 1 / dpr (initial,max,min),width = clientWidth dpr ,rem = clientWidth dpr / 10 , body font-size = dpr * 12px
对于宽度来说 750px(物理像素、分辨率) 的设计稿,假设一个div 宽度 200px,如果放在 宽度为 375 的屏幕上,设置 html 根元素 font-size: clientWidth / 750 , 1rem = 1 html font-size, div.width = 200px /html font-size (rem)
此时需要字体大小最好不要使用 rem ,使用px ,并根据dpr 设置不同大小。
因为移动端既有scale 和 dpr 的问题,所以flexiable 解决方案才会根据这两个计算一个合适的font-size值
此时需要考虑图片高清和1px 边框问题。
适配的方案:
使用完美视口,宽度自适应:使用百分比,定值或flex
动态设置viewport的scale值
媒体查询 (device-width 和 -webkit-min-device-pixel-ratio )
这些单位是视口的百分比,百分比布局
/iPhone 6尺寸作为设计稿基准
$vm_base: 375;
@function vw($px) {
@return ($px / 375) * 100vw;
}
这样的页面虽然看起来适配得很好,但是你会发现由于它是利用视口单位实现的布局,依赖于视口大小而自动缩放,无论视口过大还是过小,它也随着视口过大或者过小,失去了最大最小宽度的限制。
设计师要的1px是一个物理像素,也就是所能设计出来的最细的线,而css中的1px是一个点,dpr为2的话,那么就是两个物理像素了,这就不是设计师要的细线了。
并不是所有手机浏览器都能识别border: 0.5px;,ios7以下,android等其他系统里,0.5px会被当成为0px处理
dpr =1 和 dpr =2时的border:1px 表现形式是一样的,只有retina屏才会
解决方案:
.scale{ position: relative; } .scale:after{ content:""; position: absolute; bottom:0px; left:0px; right:0px; border-bottom:1px solid #ddd; -webkit-transform:scaleY(.5); -webkit-transform-origin:0 0; }
用@2x图,@3x
这样不是都会需要两套图片,所以到底怎样解决?
文字不能使用rem的原因是,计算后可能会出现小数,字体会出现锯齿模糊不清的情况,所以字体一般都使用真实的字体大小,但是由于高清屏的问题可能需要在不同dpr 下设置不同的字体大小
那么这个默认的宽度是怎么来的呢?不是分辨率的宽度,是移动端浏览器厂商,而在pc端,视觉视口宽度默认就是布局视口的宽度
2.3 * 4.1 in 对角线 4.7 in
未设置meta 标签 name=viewport 的content 属性值设为(width=device-width 或 initial-scale ),手机的视口会缩放以显示完全所有内容
高清屏浏览器会自动设置 window.devicePixelRatio 屏幕像素比
与 ppi 有关,也与页面的scale 有关
十、如何将750px的设计稿转换为rem,因为如果设置了scale=1,那么就是375了,直接除2的话,如果是3倍屏呢?
https://cloudinary.com/blog/how_to_automatically_adapt_website_images_to_retina_and_hidpi_devices
字体适配时使用 [dpr=1] .test {font-size: 16px} [dpr=2] .test {font-size: 32px},是在 meta : width = dpr * clientWidth,scale= 1/ dpr 时候,此时用rem 布局,但是为了防止字体计算后有小数,所以直接设置
图片高清问题:是在meta : width = device-width,scale=1,此时1css 像素不一定使用多少个物理像素表示,而图片的大小实质上相当于物理像素点,如果给一个图片设置 100px ,那么dpr =2 的设备上就是200 个物理像素,就会使图像放大,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊看起来模糊。所以对不同 dpr 引用几倍图或者在dpr =2大的设备上设置图片的包裹元素缩小50%,再或者使用rem 来设置宽度。
对于dpr=2的retina屏幕而言,1个位图像素对应于4个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊(注意上述的几个颜色值)。如:200×300(css pixel)img标签,就需要提供400×600的图片。
很明显,在普通屏幕下,200×300(css pixel)img标签,所对应的物理像素个数就是200×300个,而两倍图片的位图像素个数则是200×300*4,所以就出现一个物理像素点对应4个位图像素点,所以它的取色也只能通过一定的算法(显示结果就是一张只有原图像素总数四分之一,我们称这个过程叫做downsampling),肉眼看上去虽然图片不会模糊,但是会觉得图片缺少一些锐利度,或者是有点色差(但还是可以接受的)。
图片的像素单位是 ppi 每英寸像素点数
1px边框也是同图片高清问题同理,都是让 1css 像素等于 1 物理像素
var dpr, rem, scale;
var docEl = document.documentElement;
var fontEl = document.createElement('style');
var metaEl = document.querySelector('meta[name="viewport"]');
dpr = window.devicePixelRatio || 1;
rem = docEl.clientWidth * dpr / 7.5;//7.5取整
scale = 1 / dpr;
// 设置viewport,进行缩放,达到高清效果
metaEl.setAttribute('content', 'width=' + dpr * docEl.clientWidth + ',initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no');
// 设置data-dpr属性,留作的css hack之用
docEl.setAttribute('data-dpr', dpr);
// 动态写入样式
docEl.firstElementChild.appendChild(fontEl);
fontEl.innerHTML = 'html{font-size:' + rem + 'px!important;}';
(function flexible (window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1
// adjust body font size
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
/**
* JavaScript 检测浏览器能否处理0.5px的边框,如果可以,给html标签元素添加个class。
* div { // dpr =1
border: 1px solid #bbb;
}
// dpr = 2
.hairlines div {
border-width: 0.5px;
}
*/
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) { // 上下边框加起来为 1,元素高度,说明支持 0.5
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
[TOC]
移动端适配目的与操作思路
目的:
等比缩放,在不同屏幕宽度的设备上,以 iphone 6 为基准设置元素尺寸,更大的屏幕放大,更小的屏幕缩小。
操作思路:
常见技术点
字体:
字体的适配目标一般是:不同宽度, dpr 的设备上的字体大小看起来一致;不排除控制一行显示字体数量的目标。
字体为什么一般不用 rem ?
根据第操作思路第 3 点,假如我们在 iphone6 上设置了字体 16 px,某一行会显示6个字;那么在比 iphone 6 更宽的屏幕上字体会变大,一行也是显示6个字;我们的目的一般不是为了控制每行字的显示数量,而是希望字体的大小在不同宽度的屏幕上看起来一致,一行完全可显示更多数量的字体,显示的多少不是控制的目标。
什么时候字体需要根据 dpr 进行调整?
这个跟 meta 的设置有关系,如果页面没有进行缩放(scale),也就是没改变 1 css 代表的物理像素数,就不需要调整,因为本身 dpr 就是浏览器用来保证不同分辨率的设备上元素看起来大小一致的问题的(1css 代表的物理像素数的, dpr 高的设备, 1css 会占据更多物理像素,会看起来和 dpr =1 的设备上大小一致)。
但如果设置了 meta scale,dpr =2 的设备上,本来 1css 用 2 个物理像素表示,但设置 scale =0.5 缩小(相当于手动缩小页面),就会使 1 个物理像素 表示 1 css 像素,这个时候就需要根据 dpr 调大 css font-size 的 值,才能看起来和 dpr =1 的设备上看起来大小一致。
边框:
边框的适配目的是将 750px 设计稿的 1px 边框(实际上设计稿是物理像素)还原为物理像素。我们给边框设置的是css 像素,在 dpr =2 的设备上,1px 边框就会占据 2 * 2 个物理像素,就会比设计稿上的 1px 看起来粗一些,那实际上在 dpr =2 的设备上应该设置 css 像素 0.5 px。
图片:
什么时候需要使用 2 倍图、3 倍图?
图片本身的大小是位图像素;理论上,1个位图像素对应于1个物理像素,图片才能得到完美清晰的展示。
未设置 meta scale,当我们在 css 中使用 px 来限定一个图片的大小为 200px 200px ,在 dpr =1 的设备上1 css 像素用 dpr 个物理像素来显示,用的图片分辨率为 200 200 像素,刚好正常显示;但是如果用同一个图片到 dpr = 2 的设备上,css 的这些像素就需要 400 * 400 个物理像素来填满,就会将图片拉伸填充,图片就会变得模糊不清,所以此时就需要 @2x 倍图。
设置了 meta scale = 1/dpr, 使1px 像素用1个物理像素表示,直接使用设计稿上的图片。
解决方案有两种,
一是在不同 dpr 时引入不同的图片,这个方法需要管理更多的图片;
二是直接使用 最大分辨率的图片,在 dpr = 1 时,缩小图片,这个方法带来的问题就是缩小图片锐度会减少,另外在普通屏幕上使用大分辨率图片会造成资源浪费。
使用 rem 做移动端的适配
planA: 使用 750px 的页面布局,再缩放
设计稿尺寸:750px
meta: width = document.documentElement.clientWidth * dpr
meta: scale = 1/dpr
rem(html font-size) = (document.documentElement.clientWidth / 7.5) px,把clientWidth 分成多少份,每份为 1rem,自行决定,使 rem 成为一个好计算的值
字体:字体不使用 rem 转换,可能会出现通过rem计算,最终呈现到页面上是 23.335px 这样的奇葩的字体大小,可能还会因此出现锯齿、模糊不清等问题;在 body 中设置字体大小,会被所有页面内容继承,body font-size: 12(假设 dpr =1 时字体大小) * dpr px。
图片:仍按设计稿尺寸写,按设计稿写(假设设计稿宽为 200px , 设为 rem 单位为 200px / html font size = x rem)
边框:仍按 1px 写,按设计稿写(假设设计稿为 1px , 设为 rem 单位为 1px / html font size = x rem)
planB: 开发以 iphone 6 为基准( dpr =1时, css 像素 375px)
设计稿尺寸:750px, dpr = 3,出 375*3 分辨率的图
meta: width = device-width
meta: scale = 1
rem(html font-size) = document.documentElement.clientWidth /10 , (为计算 rem 转换方便可除 10),相当于把页面分成了10份,假设 clientWidth = 375px,每份是 37.5px,1 rem = 37.5px,j假设页面上的元素 0.2rem 就是0.2 * 37.5 = 7.5px(依旧是 css 像素),那么如何将设计稿上元素的尺寸转为 rem 呢?
注意:设置一个元素为 2px css 像素高度,不同 dpr 所呈现的物理尺寸是一致的,不同的是所占据的物理像素数不同。
字体:因为本身就是 css 像素,在不同 dpr 上所呈现的物理尺寸是一样的;
图片,需使用 n 倍图:假设图片宽高设为 200px200px,在 dpr =1的屏幕上用了一张 @1x 图片,200 200 图片分辨率(显示器点距),可以完美显示;到 dpr =2 的屏幕上后,图片是 200 200 分辨率,而 css 200px 200px表示的物理像素就是 400 400 分辨率,会把图片拉伸,占满这 400 400 个点距,图片就会变得模糊;所有设定图片的 css 尺寸后,要在不同 dpr 设备上,使用 @(dpr * 设定的 css 尺寸) 分辨率的图片,也就是 dpr 倍图
同理:若在 dpr= 1 的设备上,设定一个图片 css 尺寸为 200px 200px 却使用了400 400 分辨率的图,就会压缩图片,图片锐度。
边框:适配的目标是将边框转换为 1 个物理像素高度,写成 0.5 px 边框或使用兼容方案设置为1px height然后 transform:scale(0.5)
使用 vw,vh 做适配
设计稿:750px
https://aotu.io/notes/2017/04/28/2017-4-28-CSS-viewport-units/index.html
响应式中的媒体查询
meta 设置
断点与内容宽度
响应式中的移动端处理
使用 planB ,处理图片
参考链接:
显示器的分辨率是怎么来的