弱网真是个头疼的问题。经常有客户反馈网页加载不出来啦、白屏啦、蓝屏啦......毫无头绪之时一查网络环境,好家伙,3G网络。
且不说这种网络是否来自某个地铁中或是某台电梯里,单想象一下眼前就能浮现出这样一种画面:漫长的等待过后首先加载出了文字,然后图片就像打印出来的一样,一条一条地憋出来——
等等,图片加载一定要这么难受么?
我们看看 Medium 的效果:
图片加载相当迅速,但好像是蒙了一层纱,朦朦胧胧的。数秒过后,清晰的图片恍然浮在眼前。就像是一场梦,醒了很久还是很感动。
关于渐进式图片
最简便的方法自然是原生支持。其实常用的图片格式都支持一种渐进式(交错式)扫描格式。这种特殊的图片可以在下载时,首先加载出图片的大致轮廓,然后逐渐变清晰。在图片导出的时候,一般可以找到这么一种选项来快速提升图片加载的质量。
使用缩略图
当然,导出一张渐进式的图毕竟存在成本。要达到这种效果也可以另辟蹊径,比如Medium,大体上分为如下几步:
- 在原图片的位置贴一张同样大小的容器
- 在容器内加载一张缩略图,设置模糊效果(顺便说一下,之前的 Medium 会使用 canvas 来实现,目前已经换用了 css:filter 滤镜)
- 同时开始请求原图
- 原图下载完成后,隐藏掉缩略图容器,露出原来的图片(设置一个transition,让展现的过程更平滑)
其实,核心思想就是偷梁换柱——先用缩略图占个位,然后放心地去加载大图。通过 img 标签的 onload 事件,我们是可以知道图片什么时候加载完毕的:
<script>
function handleImageLoaded () {
// 图片加载完成
}
</script>
<img src="origin url" onload="handleImageLoaded()">
假如没有 img 标签,比如是用 background 加载的图片,那么创造一个 Image 对象也能做到:
const imgDom = new Image()
imgDom.onload = () => {
// 图片加载完成
}
imgDom.src = "origin url"
假设有这样一张原图:
首先是 img 标签,可以用 opacity 来控制缩略图的显隐,这样就可以通过 transition 使隐藏的过程平滑化。
<style>
.img-container {
position: relative;
width: 400px;
height: 200px;
}
.thumb-image {
position: absolute;
filter: blur(4px);
transition: opacity 0.3s ease;
}
</style>
<script>
function handleImageLoaded() {
// 图片加载完成
const thumbImgDom = document.getElementById('thumb')
thumbImgDom.style.opacity = 0
}
</script>
<div class="img-container">
<img class="thumb-image" id="thumb" src="img_thumb.jpg">
<img class="origin-image" src="img_origin.jpg" onload="handleImageLoaded()">
</div>
其实,只靠一个 img 标签也能实现,只不过流程换成了:展示缩略图并设置模糊-同时下载原图-原图下载成功后替换 src-关闭模糊。同时,transition 的对象由 opacity 变成了 filter。那么用单标签的方法试一次 background 图片:
<style>
.bg-image {
width: 400px;
height: 200px;
background-image: url("img_thumb.jpg");
background-size: 100% 100%;
background-repeat: no-repeat;
filter: blur(4px);
transition: filter 0.5s ease;
}
</style>
<script>
const imgDom = new Image()
imgDom.onload = () => {
// 图片加载完成
const imgDom = document.getElementById('bg-image')
imgDom.style.filter = 'none'
imgDom.style.webkitFilter = 'none'
imgDom.style.backgroundImage = 'url(img_origin.jpg)'
}
imgDom.src = "img_origin.jpg"
</script>
<div id="bg-image" class="bg-image"></div>
这样,就通过两种思路实现了一个图片模糊化加载的流程。
在公司里恰好有个项目,用h5去做一个全屏动画。涉及的切图比较多,自然就想到弱网会是什么表现:
经过优化过后,虽然加载图片的总时间没怎么变,但是看起来好了很多: