图片懒加载:从低像素预览到高清加载

老生常谈的问题,图片太多太大的网站,往往由于图片加载过慢而导致页面白屏时间过长。本次年前最后一更,来讲一个加载方法来处理这种情况。

在使用 Next.js 时,发现其支持模糊图片占位符加载的方式,本文就手动实现一个

图片解释:Nextjs 使用 placeholder="blur" 来指定当前图片使用模糊图片占位符加载

前置工作

首先你得准备一堆图片,部署一个测试的网页。

html 复制代码
<body class="p-12 m-auto">
  <div class="container flex justify-center">
    <img loading="lazy" alt="9" class="w-full object-cover rounded-lg"
      src="http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg">
      ...
      下面是并列的一堆 img,数量 100+

页面预览:

但是,当网速慢一点,或者图片再高清一点时,图片就会出现加载白屏(从上往下的扫描线)。

设置延迟加载

首先,我们利用 img 标签的能力,添加懒加载属性:

html 复制代码
<img loading="lazy" src="image.jpg" alt="..." />

兼容性:

这个策略有一个动态的原则:

  1. Lazy loading加载数量与屏幕高度有关,高度越小加载数量越少,但并不是线性关系。4G 网络下的视口距离阈值是 1250 像素,3G 网络下是 2500 像素。
  2. Lazy loading加载数量与网速有关,网速越慢,加载数量越多,但并不是线性关系
  3. 滚动会触发图片懒加载,不会说滚动一屏后再去加载。
  4. 窗口resize尺寸变化也会触发图片懒加载。
  5. 根据滚动位置不同,Lazy loading会忽略头尾的图片请求。

这个策略跟浏览器,网速和运行环境都有关系,我就以我本地为准进行测试。为了满足浏览器阈值,我们放置了 100+ 的网络图片,见上面预览图。在初始化加载时只加载了一部分,等往下滚动一些,看效果:

注意图片的嵌套层级,如果 html 书写有误,或者嵌套层级过多,懒加载可能不会被触发

配置模糊图片

我们准备好低像素的模糊图片,将每一个 img 用 div 包裹起来,并给 div 加上那个模糊的背景图片(next.js 也是这么做的):

html 复制代码
<div class="blur-load" style="background-image: url(模糊图片)">
  <img ... loading="lazy" />
</div>

其中模糊图片的样式可以这样定义, 保证背景图片与 img 能够重合:

css 复制代码
img {
  width: 100%;
  aspect-ratio: 1/1;
  object-fit: cover;
  object-position: center;
}

.blur-load {
  background-size: cover;
  background-position: center;
}

这个方法的原理是 img 图片的层级要高于其本身或者父级的背景图片,当图片加载出来后自动覆盖背景图片

制作模糊图片,可以使用 ffmpeg 工具(自行进入官网下载安装即可)。

mac 电脑直接下载 Unix 可执行文件到本地就可以用它执行指令了;windows 电脑直接下载 build 的目录文件,找到 bin 目录下的 ffmpeg.exe 就可以执行了。

运行指令:

sh 复制代码
# 我做了 20 倍缩放,你可以自己定义缩放比例
ffmpeg -i 1.jpg -vf scale=20:-1 1-sm.png

这样便生成了一个很小的图片:

调整代码:

html 复制代码
<div class="blur-load rounded-lg" style="background-image: url(./1-sm.png)">
<img loading="lazy" alt="9" class="w-full object-cover rounded-lg"
  src="http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg">
</div>

我们将网速调慢看看效果:

但是这样还有问题,就是图片是从上往下逐渐加载的,部分加载也会显示的页面上:

我们想要的效果是图片完全加载后再让图片出来,这要借助 js:

js 复制代码
function loaded(div) {
  div.classList.add('loaded');
}

const blurDivs = document.querySelectorAll('.blur-load');
blurDivs.forEach(div => {
  const img = div.querySelector('img');
  
  // 在加载完成的时候添加样式
  if (img.complete) { loaded(div) }
  else { img.addEventListener('load', () => loaded(div)) }
})

这样,在加载完成后才会有 'loaded' 样式加载在外围的 div 上。我们只需要给 div 添加样式:

css 复制代码
.blur-load.loaded > img {
 opacity: 1;
}

/* 加载完成之前保持透明 */
.blur-load > img {
  opacity: 0;
  transition: opacity 200ms ease-in-out;
}

这样在加载完成后才会有模糊图片过渡显示为原图的效果了:

自定义 loading 动画

如果你自己觉得还不满意,想要自己加入想要的动画效果,那么你可以子在这个div上自己定义样式。下面给出一种脉冲效果的参考样例:

css 复制代码
.blur-load {
  position: relative;
}
.blur-load::before {
  content: "";
  position: absolute;
  inset: 0;
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0% {
    background-color: rgba(255, 255, 255, 0);
  }
  
  50% {
    background-color: rgba(255, 255, 255, 0.4);
  }
  
  100% {
    background-color: rgba(255, 255, 255, 0);
  }
}

/* 加载完成后取消 */
.blur-load.loaded::before {
  content: none;
}

效果图如下:

相关推荐
青云交1 小时前
大数据新视界 -- 大数据大厂之 Impala 性能优化:跨数据中心环境下的挑战与对策(上)(27 / 30)
大数据·性能优化·impala·案例分析·代码示例·跨数据中心·挑战对策
风尚云网3 小时前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网
Winston Wood4 小时前
Perfetto学习大全
android·性能优化·perfetto
EterNity_TiMe_4 小时前
【论文复现】(CLIP)文本也能和图像配对
python·学习·算法·性能优化·数据分析·clip
程序猿进阶5 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
酷酷的威朗普5 小时前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5
渊兮兮5 小时前
Vue3 + TypeScript +动画,实现动态登陆页面
前端·javascript·css·typescript·动画
土豆湿6 小时前
拥抱极简主义前端开发:NoCss.js 引领无 CSS 编程潮流
开发语言·javascript·css
鸽鸽程序猿9 小时前
【前端】CSS
前端·css
工业甲酰苯胺13 小时前
Redis性能优化的18招
数据库·redis·性能优化