预备知识:
设备独立像素,以下图的iphone12 Pro为例,390*844表示的就是设备独立像素(DIP),也可以理解为CSS像素
物理像素(设备像素),就是屏幕的分辨率,显示屏就是由一个个物理像素点组成的;
DPR(Device Pixel Ratio) 设备像素比,DPR = 物理像素 / 设备独立像素
在同样的css像素大小下,屏幕有不同的dpr,同样大小的图片渲染出来的效果会不一样;因此,为了在不同的 DPR 屏幕下,让图片看起来都不失真,我们需要为不同 DPR 的图片,提供不同大小的图片。有以下几个方案
方案一:媒体查询
通过相应的媒体查询,得知当前的设备的 DPR 值,使用对应的图片
#image {background: url(xxx@1x.png)}@media (device-pixel-ratio: 2) {#image {background: url(xxx@2x.png)}}@media (device-pixel-ratio: 3) {#image {background: url(xxx@3x.png)}}
缺点:
- 可能存在一些介于 1和2,2和3 之间的 DPR 值,匹配不到对应的
- 注意:
需要注意语法需要的兼容性,需要添加前缀,譬如 -webkit-min-device-pixel-ratio,可以由 autoprefixer 插件解决
方案二:CSS 配合 image-set 语法
image-set 属于 CSS background 中的一种语法,image-set() 函数为设备提供最合适的图像分辨率,它提供一组图像选项,每个选项都有一个相关的 DPR 声明,浏览器将从中选择最适合设备的图像进行设置。
#imgage {/* 不支持 image-set 的浏览器*/background-image: url('xxx@1x.png');/* 支持 image-set 的浏览器*/background-image: image-set(url('xxx@2x.png') 2x,url('xxx@3x.png') 3x);}
缺点:
- 和方案一一样
方案三:srcset 配合像素密度描述符
img标签的srcset属性 可以根据不同的 dpr 拉取对应尺寸的图片
<div class='image'><img src='xxx@1x.png'srcset='xxx@2x.png 2x,xxx@3x.png 3x'></div>
上面 srcset 里的 2x,3x 表示 像素密度描述符,当屏幕的 dpr = 2 时,使用 xxx@2x.png.png 这张图,以此类推
缺点:
- 和方案一一样
方案四:srcset 属性配合 sizes 属性 w 宽度描述符
上述 3 种方案都存在统一的问题,只考虑了 DPR,但是忽略了响应性布局的复杂性与屏幕的多样性;
srcset 属性还有一个 w 宽度描述符,配合 sizes 属性一起使用,可以覆盖更多的面;
sizes定义图像元素在不同的视口宽度时,可能的大小值;
<imgsizes = “(min-width: 600px) 600px, 300px"src = "xxx.png"srcset = “xxx@1x.png 300w,xxx@2x.png 600w,xxx@3x.png 1200w,>
sizes = “(min-width: 600px) 600px, 300px” 的意思是:
- 如果屏幕当前的 CSS 像素宽度大于或者等于 600px,则图片的 CSS 宽度为 600px
- 反之,则图片的 CSS 宽度为 300px
srcset = “photo@1x.png 300w, photo@2x.png 600w, photo@3x.png 1200w 里面的 300w,600w,900w 叫宽度描述符。
示例:
当前屏幕 dpr = 2 ,CSS 宽度为 375px。
当前屏幕 CSS 宽度为 375px,则图片 CSS 宽度为 300px(因为sizes定义了最小宽度,屏幕375px小于600px,所以图片的CSS宽度为300px)。分别用上述 3 个宽度描述符的数值除以 300。
- 300 / 300 = 1
- 600 / 300 = 2
- 1200 / 300 = 4
上面计算得到的 1、 2、 4 即是算出的有效的像素密度,换算成和 x 描述符等价的值 。这里 600w 算出的 2 即满足 dpr = 2 的情况,匹配到xxx@2x.png这张图。
示例:
当前屏幕 dpr = 3 ,CSS 宽度为 414px。
当前屏幕 CSS 宽度为 414px,则图片 CSS 宽度仍为 300px
- 300 / 300 = 1
- 600 / 300 = 2
- 1200 / 300 = 4
dpr = 3,2 不满足,因此匹配到 1200w 这张图。
JavaScript 方案实现图片的懒加载
通过 JavaScript 实现的懒加载,主要是两种方式:
- 监听 onscroll 事件,通过 getBoundingClientRect API 获取元素图片距离视口顶部的距离,配合当前可视区域的位置实现图片的懒加载
- 通过 HTML5 的 IntersectionObserver API,Intersection Observer(交叉观察器) 配合监听元素的 isIntersecting 属性,判断元素是否在可视区内,能够实现比监听 onscroll 性能更佳的图片懒加载方案
还有一种是类似于懒加载的方式,类似于虚拟列表 - 使用 content-visibility: auto 实现图片内容的延迟渲染
<template><div class="card"><div class="img-container"><img class="img" :src="props.url" /></div><div class="title">{{ props.title }}</div><div class="area">{{ props.area }}</div><div class="collect">{{ props.collect }}人想要</div><div class="price">¥<span class="amount">{{ props.price }}</span></div></div>
</template><style>
.card {content-visibility: auto;
}
</style>
不在可视区域内的内容,一开始是没有被渲染的,在每次滚动的过程中,才逐渐渲染,以此来提升性能;虽然当前页面可视区域外的内容未被渲染,但是图片资源的 HTTP/HTTPS 请求,依然会在页面一开始被触发;因此严格来讲它并不是一个懒加载的方式;