Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for 🌟🖼️ Shiny image loading effect with Vue
Pascal Thormeier
Pascal Thormeier

Posted on

     

🌟🖼️ Shiny image loading effect with Vue

You've seen it at least a couple of times already: Those shiny box effects when images take a little longer to load. They're on news sites, blogs, shops, you name it.In this article, I'll explain how to build a component in Vue that offers this effect!

Some scaffolding

I'll start with a new component I callLoadingBoxImage.vue - this will be a single file component, so it can be used within any Vue or Nuxt app afterwards. First, I'll basically wrap the<img> tag:

<!-- LoadingBoxImage.vue --><template><img:src="src":alt="alt"/></template><script>exportdefault{props:{src:{type:String,required:true},alt:{type:String,required:true}}}</script>
Enter fullscreen modeExit fullscreen mode

Now I'll add some styling to this, so it behaves well and more responsive:

<stylescoped>img{max-width:100%;}</style>
Enter fullscreen modeExit fullscreen mode

This component can be used like this:

<!-- App.vue --><template><divid="app"><loading-box-imagesrc="http://via.placeholder.com/3200x2400"alt="Some image"/></div></template><script>importLoadingBoxImagefrom'./components/LoadingBoxImage'exportdefault{components:{LoadingBoxImage}}</script>
Enter fullscreen modeExit fullscreen mode

So far, so good. I've replicated the standard image tag as a Vue component.

Next up: Adding a box and removing it again.

The placeholder

The placeholder box will be an aria-hidden div next to the image. I'll hide it once the image has loaded via the nativeload event and a flag:

<template><divclass="image-container"><img:src="src":alt="alt"@load="loaded"/><divclass="image-placeholder":class="{ hidden: isLoaded }"aria-hidden="true"/></div></template><script>exportdefault{props:{src:{type:String,required:true},alt:{type:String,required:true}},data(){return{isLoaded:false}},methods:{loaded(){this.isLoaded=true}}}</script><stylescoped>.image-container,img{max-width:100%;}.image-placeholder.hidden{display:none;}</style>
Enter fullscreen modeExit fullscreen mode

I also needed to add a container around the image and its placeholder and did some adjustments to the styling.

Now, ideally, the placeholder should be the same size as the image, right? I've got two options here: Use fixed dimensions or try to fetch them before the image has fully loaded. Since the latter sounds a lot fancier, I'll implement this first.

At some point, the image will havenativeWidth andnativeHeight available, so I can use those to calculate an aspect ratio:

// LoadingBoxImage.vue, script partmounted(){constimg=this.$refs.img// Constantly poll for the naturalWidthconstsizeInterval=setInterval(()=>{if(img.naturalWidth){// As soon as available: Stop pollingclearInterval(sizeInterval)// Calculate image ratiothis.loadedAspectRatio=img.naturalWidth/img.naturalHeight}},10)}
Enter fullscreen modeExit fullscreen mode

(I also addedref attributes to the original<img> tag and the placeholder to be able to fetch the necessary data)

I can use that now to calculate the placeholder's height. To make it more responsive, I'm updating the client width on the window'sresize event and set it once when mounted:

// ...data(){return{isLoaded:false,loadedAspectRatio:null,clientWidth:0,};},methods:{loaded(){this.isLoaded=true;},updateClientWidth(){this.clientWidth=this.$refs.placeholder.clientWidth;}},computed:{/**     * Calculates the height of the placeholder     * via the images nativeWidth and nativeHeight.     */placeholderHeight(){if(!this.loadedAspectRatio){return0;}returnthis.clientWidth/this.loadedAspectRatio;},},mounted(){constimg=this.$refs.img;constsizeInterval=setInterval(()=>{if(img.naturalWidth){clearInterval(sizeInterval);this.loadedAspectRatio=img.naturalWidth/img.naturalHeight;}},10);window.addEventListener('resize',this.updateClientWidth)this.updateClientWidth()},// ...
Enter fullscreen modeExit fullscreen mode

And set this on the placeholder:

<!-- ... --><divclass="image-placeholder":class="{ hidden: isLoaded }"aria-hidden="true"ref="placeholder":style="{ height: `${placeholderHeight}px` }"/><!-- ... -->
Enter fullscreen modeExit fullscreen mode

Ok, now I've got a placeholder the same size as the image! Awesome! Now I can start to...

Add the shiny box effect

This can be done with CSS keyframes and linear gradient:

.image-placeholder{background:rgba(0,0,0,.2);background-image:linear-gradient(120deg,rgba(255,255,255,0)0%,rgba(255,255,255,0)40%,rgba(255,255,255,0.8)50%,rgba(255,255,255,0)60%,rgba(255,255,255,0)100%);background-position-x:-100vw;background-repeat:no-repeat;animation:shiny1.5sinfinite;}@keyframesshiny{0%{background-position-x:-100vw;}10%{background-position-x:-100vw;}75%{background-position-x:100vw;}100%{background-position-x:100vw;}}
Enter fullscreen modeExit fullscreen mode

This will add a single reflection that moves periodically from left to right to an otherwise grayed element.

And that's it!

Here's a Codesandbox to see it in action (I'm not hiding the placeholder for you to see what it would look like):

I'm sure that the gradient and the timing can still be tweaked, though. Also some use cases, like smaller images and complete accessibility are missing, but I'm certain that this can be used as a starting point.

Happy holidays!


I hope you enjoyed reading this article as much as I enjoyed writing it! If so, leave a ❤️or a 🦄! I write tech articles in my free time and like to drink coffee every once in a while.

If you want to support my efforts, please considerbuying me a coffeeorfollow me on Twitter 🐦!

Buy me a coffee button

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Passionate full stack web developer, course author for Educative, book author for Packt, he/him.Find my work and get to know me on my Linktree: https://linktr.ee/thormeier
  • Location
    Switzerland
  • Education
    BSc FHNW Computer Science (iCompetence)
  • Work
    Senior Software Developer at Liip
  • Joined

More fromPascal Thormeier

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp