Medium-like Image Loading with Vue.js (part 1)
Louis Zawadzki4 min read
When I’m stuck on a train or queuing at the supermarket I usually read one or two articles on Medium.
There’s plenty of stuff that I really love about Medium. Like the email they send me every morning. Or the personal recommendations on their main page.
And the blurry image loading. I mean, seriously, the first time I noticed it I was like:
If you don’t know what I’m talking about, click on this link and see how the top image is displayed.
I recently started using Vue.js and I thought, well, let’s see if we can build a Vue.js component to do this!
How it works
So I wondered, how do this thing work? Fortunately José M. Perez has done a wonderful (Medium) blog post explaining the principle of this technique. He even provided us with a plain javascript implementation.
If you don’t want to read the whole article, the core principle is really simple:
- Download a low-resolution image and scale it to the real size (your browser will take care of the blur)
- Once your real image is downloaded, put it instead of the low-res one
Let’s start
For our example, imagine our HTML looks just like this:
<!DOCTYPE html>
<html>
<head>
<style>
img {
width: 100%;
}
</style>
</head>
<body>
<img
src="https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg"
></img>
</body>
</html>
It’s very simple: it displays one image.
First let’s include Vue.js, our .js script and substitute the img
tag by a custom one that will call our component, such as blurry-image-loader
:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<style>
img {
width: 100%;
}
</style>
</head>
<body>
<blurry-image-loader
src="https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg"
small-src="https://cdn-images-1.medium.com/freeze/max/27/1*sg-uLNm73whmdOgKlrQdZA.jpeg?q=20"
></blurry-image-loader>
<script src="vue-blurry-image-loader.js"></script>
</body>
</html>
Yay! Now that our HTML is ready, let’s create our script! First of all, we have to create a new Vue instance and to register our component:
Vue.component('blurry-image-loader', {})
new Vue({
el: 'body'
})
Allright. As you probably saw earlier, our HTML component has two attributes: the url of the image in full size (src
) and the smaller image (small-src
). We can register those attributes as props in our Vue.js component:
Vue.component('blurry-image-loader', {
props: [
'src',
'smallSrc'
]
})
N.B.: we have to use camelCase in javascript, so small-src
becomes smallSrc
.
Ok let’s go on step-by-step: let’s say the template of our component will be an image with the low-res image:
Vue.component('blurry-image-loader', {
props: [
'src',
'smallSrc'
],
template: '<img :src=smallSrc></img>'
})
So now if you open your HTML file in your browser, you should see a blurry image. But that’s not going to work with that template, we need the src
attribute of the img
element to change when our real image is loaded. So we need a kind of changing props, which is in fact a … data!
Let’s call it imageSrc
and initialize it to the value of smallSrc
:
Vue.component('blurry-image-loader', {
props: [
'src',
'smallSrc'
],
data: function () {
return {
imageSrc: this.smallSrc
}
},
template: '<img :src=imageSrc></img>'
})
Good! We’re nearly there.
Now we need to load the real image when the component is created, and once this is done we have to change the value of imageSrc
. Vue.js components have a ready
attribute which gives us the possibility to execute a function once the component is ready. I believe this sounds like the right place to do our loading!
(N.B.: in Vue 2 the ready atribute is now called mounted)
(N.B.: as pointed out by Bokkeman in the comments, to prevent trouble with your browser cache set your image src attribute after the onload event handler)
Vue.component('blurry-image-loader', {
props: [
'src',
'smallSrc'
],
data: function () {
return {
imageSrc: this.smallSrc
}
},
template: '<img :src=imageSrc></img>',
// use mounted in Vue.js 2.0
ready: function () {
var img, that
img = new Image()
that = this
img.onload = function(){
that.imageSrc = that.src
}
img.src = this.src
}
})
And voilà! We have achieved this with actually fewer lines of code than I thought!
You can check out my Codepen to see it live (I’ve added a small timeout to make sure you see the blurry image).
I you liked this article please share it and keep checking out this blog, I’m currently writing part 2 which will show how to smooth the unblurring!
To discover how to achieve the exact same effect with transitions head to part 2 over here.