Medium 是如何加载图片的?
原文出处:追客。 ps 还是来点前端知识。。
首先,我们来看看 Medium 加载图片的效果是怎样子的:
我的博客首页也实现了这种效果。下面我们就分析如何实现。
How
我们随便打开 Medium 中有图片的页面,然后 审查元素,就可以得到:
简化之后得到:
<section>
<div class="placeholder"></div>
<div class="prossiveMedia">
<img class="img-samll">
<canvas></canvas>
<img class="img-large">
</div>
</section>
实现的过程大致如下:
-
使用一个 placeholder 来防止图片 collapse。
-
先加载一个很小的图片
-
当这个小图片加载完毕之后,把他画在 里面,然后把图片弄模糊。
-
当大图片加载完毕之后,显示他。
Do it
首先,HTML 结构如下:
<section class="banner">
<div class="placeholder"></div>
<img
class="img-small"
src="https://cdn-images-1.medium.com/freeze/max/27/1*sg-uLNm73whmdOgKlrQdZA.jpeg?q=20"
data-large="https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg"
>
</section>
.placeholder 是用来撑起 .banner 的高度。因为如果我们想要让两张图片重叠的话,会用到 position: absolute,定位元素会脱离文档流,那么 .banner 的高度就会为 0。
.img-small 首先加载的是小图片。大图片的 URL 放在了 data-large 属性里面。
接下来是,CSS部分:
.banner {
position: relative;
overflow: hidden;
}
.placeholder {
padding-bottom: 66.6%;
}
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
opacity: 0;
transition: opacity 1s linear;
}
img.loaded {
opacity: 1;
}
.img-small {
filter: blur(50px);
-webkit-filter: blur(50px);
}
.banner 设置了 relative,所以他成为了他 children (.placeholder, img) 的containning block 了。
注意到,.placeholder 设置了 padding-bottom: 66.6%。66.6% 是相对于他的containning block 的宽度,也就是 .banner 的宽度。
这个值应该和 img 的高度一样,这样一来,.banner 的高度就是 img 的高度了。
利用 transition 让透明度变化有个渐变效果。
实现模糊效果是使用了 filter: blur(),而没有使用 。
然后,JavaScript 代码部分比较简单:
const app = (() => {
const imgSmall = document.querySelector('.img-small')
const loadImage = () => {
const img = new Image()
img.src = imgSmall.src
img.addEventListener('load', (e) => {
imgSmall.classList.add('loaded')
}, false)
const imgLarge = new Image()
imgLarge.src = imgSmall.dataset.large
imgLarge.addEventListener('load', (e) => {
imgLarge.classList.add('loaded')
}, false)
imgSmall.parentNode.appendChild(imgLarge)
}
return {
loadImage
}
})()
app.loadImage()
需要注意的是,我们不能直接监听 .img-small 的 load 事件,要 new Image,然后赋予其 src,然后我们就可以监听他的 load 事件了。
最后,为了凸显效果,我们要禁止缓存和设置较差的网络环境。具体做法如图:
实际效果如下: