aFarkas / lazysizes

High performance and SEO friendly lazy loader for images (responsive and normal), iframes and more, that detects any visibility changes triggered through user interaction, CSS or JavaScript without configuration.
MIT License
17.53k stars 1.73k forks source link

LQIP technique using css background images #35

Closed HybridSolutions closed 9 years ago

HybridSolutions commented 9 years ago

Hi,

first of all, congratulations for this awesome plugin! Very nice work. I would like to ask you if is possible to use LQIP technique while using background images instead of img tags. For now, I'm using jQuery Cover plugin (https://github.com/greenish/jquery-cover), but it would be great to have this feature out of the box. The advantage of using background images with css is that you can have cover mode support without additional scripts.

Thanks Hugo

aFarkas commented 9 years ago

@HybridSolutions First of all thx. Doesn't the unveilhooks plugin, the bgset plugin or the addClasses feature work for this use case?

aFarkas commented 9 years ago

Here the example for the addClasses feature:

<script>
window.lazySizesConfig = {
    addClasses: true
};
</script>

<style>
    .bg-stage {
        background-repeat: no-repeat;
        background-position: center;
        background-size: cover;
    }
    .bg-stage.lazyloaded {
        background-image: url(lazyloaded-bg.jpg);
    }
</style>

<div class="bg-stage lazyload">
    <!-- content -->
</div>
HybridSolutions commented 9 years ago

Just to make sure I understood, inside the div I would load a low quality image using css, or I could even have

.bg-stage { background-repeat: no-repeat; background-position: center; background-size: cover; background-image: url(lowqualityimage-bg.jpg); }

Is that it?

aFarkas commented 9 years ago

@HybridSolutions

Oh, I misread your question. Well the addClasses feature simply just changes the class so if the lquip background isn't loaded, it won't serve as a lqip source.

But the unveilhooks and the bgset plugin should work for you. And change the background-image more lazy (i.e.: with lqip source).

aFarkas commented 9 years ago

This means:


<style>
.bg-stage {
background-repeat: no-repeat;
background-position: center;
background-size: cover;
background-image: url(lowqualityimage-bg.jpg);
}
</style>

<div class="lazyload" data-bg="cover-image.jpg">
</div>
aFarkas commented 9 years ago

So re-read your last question. Of course you could also use a second inner div, which is covered as soon as the normal source is loaded. (Nice thought!) I hope you have enough possible patterns to accomplish the task with lazysizes?

aFarkas commented 9 years ago

For the inner lqip source you could also use :after / :before

HybridSolutions commented 9 years ago

Thanks! I'll try that out. If I can pull this off just using your plugin and css it will be great!

HybridSolutions commented 9 years ago

Well, addClasses can't be used with multiple images since the bg image would be common to all the items with bg-stage class. Also i noticed that the class lazyloaded is placed in the object before the image is fully loaded, so any inner div containing a low quality bg is immediately overlapped by a black background with a progressively loading HQ image on top (the jpg file does not have progressive enconding in it). Even if it worked it would require a unique class for each item of the carousel for this approach to work.

Using the unveilhooks it's not working either, since it only supports one image, and the loading of the low quality images is not managed so they don't get fully loaded first.

image

// First item of the carousel
<div class="item active">
                <div class="fill lazyload" data-bg="img/photo-bg-1.jpg" style="background-image:url('img/photo-bg-1-LQ.jpg')></div>
            </div>
</div>
//Second item of the carousel
...

I'm not seeing a solution for this without using an img tag. It would be awesome to be able to have something like:

<div class="fill lazyload" data-bg="img/photo-bg-1.jpg" data-bg-lq="img/photo-bg-1-LQ.jpg"></div>

and have the low quality bg image replaced only after the high quality one is fully loaded.

aFarkas commented 9 years ago

@HybridSolutions For the addClasses part I really thought about multiple classes combination i.e: .stage-1.lazyloaded, .stage-2.lazyloaded. All .stage-n have the same structure but can have different background images.

Wether you choose this or the unveilhooks plugin is really a matter of seperation of concerns, if those are typicall style things you should use addClasses, but if it is something, that is better known by the CMS it should be rendered into the HTML as data-bg. About the unveilhooks, this is actually handled and you should see it. The unveilhooks plugin does not look at the lqip source, but it simply loads the larger image in background (without attaching the background-image style) and only if this is loaded successfully the background style is changed. So everything should be good here.

HybridSolutions commented 9 years ago

If you try to see the loading process using Google Chrome Developer tools with a slow network speed, you will see that the lazyloaded class is applied before the image has loaded, so the switch happens right away and you get a black background on top of the low quality image. Also you need to have 2 divs for this to work. AddClasses is not the right way with a carousel.

unveilhooks could be a nice solution but it doesn't control when the low quality image gets loaded, so you don't get the "replace" visual effect. You get the full image loading slowly effect.

image

Am I doing something wrong?

aFarkas commented 9 years ago

Ok, just realized, I had a small issue in the unveilhooks plugin. Could you use the newest version of lazysizes + unveilhooks:

Here is also a jsfiddle: http://jsfiddle.net/trixta/bhx3n0uL/embedded/result/

aFarkas commented 9 years ago

Also note: That I have done some great improvement there about lazy preloading. Because lqip is nice, but having the better picture sooner is much better.

aFarkas commented 9 years ago

And maybe think of using the data-bgset plugin, if you are doing a responsive website. And the images are that important and so much. You can create 2-4 image widths: (i.e: 640, 980, 1280) to save bandwidth

HybridSolutions commented 9 years ago

Meanwhile I think i found a solution for the AddClasses approach + unveilhooks!! Found this article (http://css-tricks.com/forums/topic/background-image-replacement-removes-background/) that solves the issue with AddClasses and the initial low res image getting removed until the high resolution version is loaded. You should have this huge tip on documentation :) Seems it's not working on IE 8 (only LQ version is shown).

<div class="item active">
                <div class="img1 fill lazyload" data-bg="<% =CaminhoSkin %>img/photo-bg-1-LQ.jpg"></div>
            </div>
            <div class="item">
                <div class="img2 fill lazyload" data-bg="<% =CaminhoSkin %>img/photo-bg-2-LQ.jpg"></div>              
            </div>
            <div class="item">
                <div class="img3 fill lazyload" data-bg="<% =CaminhoSkin %>img/photo-bg-3-LQ.jpg"></div>              
            </div>

and this for the css

.img1.lazyloaded {    
    background-image: url(img/photo-bg-1.jpg),url(img/photo-bg-1-LQ.jpg) !important;        
}
.img2.lazyloaded {    
    background-image: url(img/photo-bg-2.jpg),url(img/photo-bg-2-LQ.jpg) !important; 
}
.img3.lazyloaded {    
    background-image: url(img/photo-bg-3.jpg),url(img/photo-bg-3-LQ.jpg) !important; 
}

The best part is this:

image

As you can see, now your plugin makes sure to deliver the LQ version as soon as the slide comes in to view while the HQ images are loading! Also, the HQ image appears slowly on top of the LQ one.

Still have to check this on a mobile device.

I will update to the new version and let you know if changed anything.

aFarkas commented 9 years ago

@HybridSolutions IE8 is not supported anymore! If you need to support, don't use this!

HybridSolutions commented 9 years ago

With the new version when using data attributes for the image, AddClasses stops adding lazyloaded class. This breaks my previous post. Does it have to work like this?

aFarkas commented 9 years ago

Yeah, I just realized this, but it is a feature, not a bug. You can workaround this problem:

.img1.lazyloaded,
.img1.lazyloading {    
    background-image: url(img/photo-bg-1.jpg),url(img/photo-bg-1-LQ.jpg) !important;        
}
.img2.lazyloaded,
.img2.lazyloading {    
    background-image: url(img/photo-bg-2.jpg),url(img/photo-bg-2-LQ.jpg) !important; 
}
.img3.lazyloaded,
.img3.lazyloading {    
    background-image: url(img/photo-bg-3.jpg),url(img/photo-bg-3-LQ.jpg) !important; 
}

Or now you can still do it the old way and don't need to use multiple backgrounds anymore.

.img1 {    
    background-image: url(img/photo-bg-1-LQ.jpg);        
}
/* img2, img3 ... */
HybridSolutions commented 9 years ago

Ok, just tested and worked very well without the AddClasses approach. Basically, I got even better behavior than in the previous AddClasses + unveilhooks approach. Don't know what you did, but now all the images load a lot faster. With slow connections, the LQ images load only when the slide comes in while the HQ load in the background. Huge improvement here! :)

image

With a fast connection, LQ images don't even get loaded :) Nice!

image

I'll run a couple more tests to check everything is ok, but i have to thank you for your effort! :+1:

Note: Don't know why, but when I use the unveilhooks plugin, i get these errors on Google Chrome Developer Tools. Everything works but they are there.

image

aFarkas commented 9 years ago

I can't reproduce the error in chrome (neither in 39 and in 41). Can you re-test this in incognito mode. And can you reproduce the error also on this page: http://jsfiddle.net/trixta/bhx3n0uL/embedded/result/

HybridSolutions commented 9 years ago

Sorry, forgot to mention, that the errors only happen when using Device Mode. Out of device mode, there are no errors. Here's a screen capture showing these errors while in device mode.

image

But ignore this, since I tested with Google's website and the errors also appear. Might be a Chrome bug.

Once again, Thanks!!

rulatir commented 6 years ago

It's worrying that all these examples require hardcoding the URLs in stylesheets separately for each image. This doesn't scale and requires jumping through hoops, e.g. emitting chunks of code for "doing" one image in two places: in the stylesheet and in the body, or writing JS that manages these style rules.

ToshY commented 9 months ago

I've found the approach using background-image: url(image.jpg),url(lqip.jpg); actually gives visually a good result for background image lazyloading. If you use it as an inline style instead of hardcoding in stylesheet itself, it becomes more dynamic.

<div class="lazyload" data-bg="image.jpg" style="background-image: url(image.jpg),url(lqip.jpg);"></div>
// after class `lazyloading` changed to `lazyloaded` it shows in the DOM as following
<div class="lazyloaded" data-bg="image.jpg" style="background-image: url(image.jpg);"></div>