设备独立像素
以 iPhone6/7/8 为例,这里我们打开 Chrome 开发者工具: 这里的 375 * 667 表示的是什么呢,表示的是设备独立像素(DIP),也可以理解为 CSS 像素,也称为逻辑像素: 设备独立像素 = CSS 像素 = 逻辑像素 如何记忆呢?这里使用 CSS 像素来记忆,也就是说。我们设定一个宽度为 375px 的 div,刚好可以充满这个设备的一行,配合高度 667px ,则 div 的大小刚好可以充满整个屏幕
物理像素
OK,那么,什么又是物理像素呢。我们到电商网站购买手机,都会看一看手机的参数,以 JD 上的 iPhone7 为例: 可以看到,iPhone7 的分辨率是 1334 x 750,这里描述的就是屏幕实际的物理像素。 物理像素,又称为设备像素。显示屏是由一个个物理像素点组成的,1334 x 750 表示手机分别在垂直和水平上所具有的像素点数。通过控制每个像素点的颜色,就可以使屏幕显示出不同的图像,屏幕从工厂出来那天起,它上面的物理像素点就固定不变了,单位为 pt。 设备像素 = 物理像素
DPR(Device Pixel Ratio) 设备像素比
OK,有了上面两个概念,就可以顺理成章引出下一个概念。DPR(Device Pixel Ratio) 设备像素比,这个与我们通常说的视网膜屏(多倍屏,Retina 屏)有关。 设备像素比描述的是未缩放状态下,物理像素和设备独立像素的初始比例关系。 简单的计算公式: DPR = 物理像素 / 设备独立像素 我们套用一下上面 iPhone7 的数据(取设备的物理像素宽度与设备独立像素宽度进行计算): iPhone7’s DPR = iPhone7’s 物理像素宽度 / iPhone7's 设备独立像素宽度 = 2 提示:750 / 375 = 2 或者是 1334 / 667 = 2 可以得到 iPhone7 的 dpr 为 2。也就是我们常说的视网膜屏幕。
视网膜(Retina)屏幕是苹果公司"发明"的一个营销术语。苹果公司将 dpr > 1 的屏幕称为视网膜屏幕。
适配涵义
- 适配不同屏幕大小,也就是适配不同屏幕下的 CSS 像素
- 适配不同像素密度,也就是适配不同屏幕下 dpr 不一致导致的一些问题
适配不同屏幕大小
百分比
百分比值总要相对于另一个量,比如长度。每个允许使用百分比值的属性,同时也要定义百分比值参照的那个量。这个量可以是相同元素的另一个属性的值,也可以是祖先元素的某个属性的值,甚至是格式化上下文的一个度量(比如包含块的宽度)。 具体来说: 宽度(width)、间距(maring/padding)支持百分比值,但默认的相对参考值是包含块的宽度;
高度(height)百分比的大小是相对其父级元素高的大小;
边框(border)不支持百分值;
边框圆角半径(border-radius)支持百分比值,但水平方向相对参考值是盒子的宽度,垂直方向相对参考值是盒子的高度;
文本大小(font-size)支持百分比值,但相对参考值是父元素的 font-size 的值;
盒阴影(box-shadow)和文本阴影(text-shadow)不支持百分比值;
首先,支持百分比单位的度量属性有其各自的参照基准,其次并非所有度量属性都支持百分比单位。所以我们需要另辟蹊径。
rem 适配方案 (flexible)
根据网页的根元素来设置字体大小,和 em(font size of the element)的区别是,em 是根据其父元素的字体大小来设置,而 rem 是根据网页的跟元素(html)来设置字体大小。
根据 document.documentElement.clientWidth 动态修改 html 的 font-size ,页面其他元素使用 rem 作为长度单位进行布局,从而实现页面的等比缩放 (flexible)
vw 适配方案
百分比适配方案的核心需要一个全局通用的基准单位,rem 是不错,但是需要借助 Javascript 进行动态修改根元素的 font-size,而 vw/vh(vmax/vmin) 的出现则很好弥补 rem 需要 JS 辅助的缺点。
vw 等于初始包含块(html 元素)宽度的 1%,也就是:
- 1vw 等于 window.innerWidth 的数值的 1%
- 1vh 等于 window.innerHeight 的数值的 1%
自动转换插件
当我们使用 rem 作为长度单位的时,通常会有借助 Sass/Less 实现一个转换函数,像是这样:
// 假设设计稿的宽度是 375px,假设取设计稿宽度下 1rem = 100px
$baseFontSize: 100;
@function px2rem($px) {
@return $px / $baseFontSize * 1rem;
}
同理,在 vw 方案下,我们只需要去改写这个方法:
// 假设设计稿的宽度是 375px
@function px2rem($px) {
@return $px / 375 * 100vw;
}
当然,我们还可以借助一些插件包去实现这个自动转换,提高效率,譬如 postcss-px-to-viewport
1px 线
不是根据设备的 dpr 动态改写 meta 标签,设置 viewport 的缩放来实现适配,在 Retina 屏下,无法很好的展示真正的 1px 物理像素线条。
设计师想要的 retina 下 border: 1px,其实是 1 物理像素宽,而不是 1 CSS 像素宽度,对于 CSS 而言:
在 dpr = 1 时,此时 1 物理像素等于 1 CSS 像素宽度;
在 dpr = 2 时,此时 1 物理像素等于 0.5 CSS 宽度像素,可以认为 border-width: 1px 这里的 1px 其实是 1 CSS 像素宽度,等于 2 像素物理宽度,设计师其实想要的是 border-width: 0.5px;
在 dpr = 3 时,此时 1 物理像素等于 0.33 CSS 宽度像素,设计师其实想要的是 border: 0.333px
然而,并不是所有手机浏览器都能识别 border-width: 0.5px,在 iOS7 以下,Android 等其他系统里,小于 1px 的单位会被当成为 0px 处理,那么如何实现这 0.5px、0.33px 呢? 这里介绍几种方法:
渐变实现
使用缩放实现
使用图片实现(base64)
使用 SVG 实现(嵌入 background url)
Retina 屏幕下 1px 线的实现。
图片适配及优化
图像通常占据了网页上下载资源绝的大部分。优化图像通常可以最大限度地减少从网站下载的字节数以及提高网站性能。 通常可以,有一些通用的优化手段:
消除多余的图像资源
尽可能利用 CSS3\SVG 矢量图像替代某些光栅图像
谨慎使用字体图标,使用网页字体取代在图像中进行文本编码
选择正确的图片格式
为不同 DPR 屏幕提供最适合的图片尺寸
无脑多倍图
在移动端假设我们需要一张 CSS 像素为 300 x 200 的图像,考虑到现在已经有了 dpr = 3 的设备,那么要保证图片在 dpr = 3 的设备下也正常高清展示,我们最大可能需要一张 900 x 600 的原图。 这样,不管设备的 dpr 是否为 3,我们统一都使用 3 倍图。这样即使在 dpr = 1,dpr = 2 的设备上,也能非常好的展示图片。 当然这样并不可取,会造成大量带宽的浪费。现代浏览器,提供了更好的方式,让我们能够根据设备 dpr 的不同,提供不同尺寸的图片
srcset 配合 1x 2x 像素密度描述符
简单来说,srcset 可以根据不同的 dpr 拉取对应尺寸的图片:
<div class='illustration'>
<img src='illustration-small.png'
srcset='images/illustration-small.png 1x,
images/illustration-big.png 2x'
style='max-width: 500px'/>
</div>
srcset 属性配合 sizes 属性 w 宽度描述符
上面 1x,2x 的写法比较容易接受易于理解。 除此之外,srcset 属性还有一个 w 宽度描述符,配合 sizes 属性一起使用,可以覆盖更多的面。
<img
sizes = “(min-width: 600px) 600px, 300px"
src = "photo.png"
srcset = “photo@1x.png 300w,
photo@2x.png 600w,
photo@3x.png 1200w,
>
解析一下: sizes = “(min-width: 600px) 600px, 300px" 的意思是,如果屏幕当前的 CSS 像素宽度大于或者等于 600px,则图片的 CSS 宽度为 600px,反之,则图片的 CSS 宽度为 300px。 也就是 sizes 属性声明了在不同宽度下图片的 CSS 宽度表现。这里可以理解为,大屏幕下图片宽度为 600px,小屏幕下图片宽度为 300px。(具体的媒体查询代码由 CSS 实现) 这里的 sizes 属性只是声明了在不同宽度下图片的 CSS 宽度表现,而具体使图片在大于 600px 的屏幕上展示为 600px 宽度的代码需要另外由 CSS 或者 JS 实现,有点绕。 srcset = “photo@1x.png 300w, photo@2x.png 600w, photo@3x.png 1200w 里面的 300w,600w,900w 叫宽度描述符。怎么确定当前场景会选取哪张图片呢?
- 当前屏幕 dpr = 2 ,CSS 宽度为 375px。
当前屏幕 CSS 宽度为 375px,则图片 CSS 宽度为 300px。分别用上述 3 个宽度描述符的数值除以 300。 300 / 300 = 1
600 / 300 = 2
1200 / 300 = 4
上面计算得到的 1、 2、 4 即是算出的有效的像素密度,换算成和 x 描述符等价的值 。这里 600w 算出的 2 即满足 dpr = 2 的情况,选择此张图。
- 当前屏幕 dpr = 3 ,CSS 宽度为 414px。 当前屏幕 CSS 宽度为 414px,则图片 CSS 宽度仍为 300px。再计算一次: 300 / 300 = 1
600 / 300 = 2
1200 / 300 = 4
因为 dpr = 3,2 已经不满足了,则此时会选择 1200w 这张图。
- 当前屏幕 dpr = 1 ,CSS 宽度为 1920px。 当前屏幕 CSS 宽度为 1920px,则图片 CSS 宽度变为了 600px。再计算一次: 300 / 600 = .5
600 / 600 = 1
1200 / 600 = 2
因为 dpr = 1,所以此时会选择 600w 对应的图片。 具体的可以试下这个 Demo:CodePen Demo -- srcset 属性配合 w 宽度描述符配合 sizes 属性 此方案的意义在于考虑到了响应性布局的复杂性与屏幕的多样性,利用上述规则,可以一次适配 PC 端大屏幕和移动端高清屏,一箭多雕