Skip to content

Lazy load responsive background images

optimalisatie edited this page Aug 2, 2020 · 4 revisions

$lazy can be used to lazy load responsive background images, both in HTML and in stylesheets using its $lazybg extension. This Wiki shows how to lazy load responsive background images.

To create responsive background images requires an advanced HTML filter and a solution to generate responsive images from a background-image source file. The Style.Tools PHP library and CMS plugins provide an advanced image editing solution based on Sharp.js that can easily generate optimized responsive images with support for Google WebP. The Style.Tools image editing solution can generate +100 responsive images simultaneously in a few seconds.

The following example shows how to generate responsive images using the Style.Tools PHP library.

/**
 * Setup Style.Tools HTML filter
 */
\StyleTools\HTML::add_filter('pre', function ($HTML) {

    // background images
    $HTML = preg_replace_callback('#<div[^>]+background(-image[^>]+|\s*:\s*)url\s*\(\s*([^\)]+)\)[^>]+>#smi', function ($matches) {
        $tag = $matches[0];
        $img_src = trim($matches[2], ' \'"');

        // local images only
        if ($img_src && !preg_match('#^(http(s)?:|//)#Ui', $img_src)) {

            // sizes for the responsive background image
            $sizes = array(
                array(411,514),
                array(553,514),
                array(768,462),
                array(1024,425),
                array(1280,425),
                array(1440,425)
            );

            // generate responsive images
            $imgs = array();
            foreach ($sizes as $size) {
                try {
                    $img = \StyleTools\Images::optimize($img_src, json_decode('{
     "optimize": [{
         "method": "api",
         "arguments": [{
             "url": "https://europe-west1-xxx.cloudfunctions.net/optimize-images",
             "headers": {
                 "x-styletools-security-key": "xxx"
             },
             "options": {
                 "handlers": [{
                     "type": "flatten",
                     "arguments": [{
                         "background": {
                             "r": 255,
                             "g": 255,
                             "b": 255
                         }
                     }]
                 }, {
                     "type": "jpeg"
                 }, {
                     "type": "imagemin",
                     "plugins": [{
                         "plugin": "mozjpeg",
                         "options": {
                             "quality": 95
                         }
                     }]
                 }]
             }
         }]
     }],
     "webp": {
         "quality": 95
     },
     "srcset": "auto",
     "srcset_resize": {
         "fit": "cover",
         "height": "aspect"
     },
     "default_format": "jpg",
     "src_tags": {
         "img": [
             "src",
             "srcset"
         ]
     },
     "path": {
         "root": "/i/",
         "hash_filename": {
             "attr": "alt"
         },
         "hash_extension": "jpg"
     }
 }', true));
                    $imgs[] = array($img,$size[0]);
                } catch (\Exception $err) {
                    \StyleTools\Error::add($err);
                }
            }

            $tag = \StyleTools\HTML::attr('data-rzbg', $tag, htmlentities(json_encode($imgs), ENT_COMPAT, 'utf-8'));
        }

        return $tag;
    }, $HTML);

    return $HTML;
});

The result of the above HTML filter is the following:

<!-- before -->
<div style="background-image:url('/image.jpg');">...</div>

<!-- after -->
<div data-rzbg='[["/i/image-w411.jpg",411],["/i/image-w553.jpg",553],...]'>...</div>

The following $lazy code shows how to lazy load the responsive background images with support for view-port changes.

// lazy load *[data-zbg] elements
$lazy('[data-rzbg]', function(el) {
    function render_responsive_bg(e) {
        if (e) {
            if (e.matches) {
                // media query still matches
                return;
            }
            mqMatch.removeListener(render_responsive_bg);
        }

        var match;
        bg.forEach(function(i) {
            if (!match) {
                if (i[1]) {
                    mqMatch = window.matchMedia(((!isNaN(parseInt(i[1]))) ? '(max-width: ' + i[1] + 'px)' : i[1]));
                    if (mqMatch.matches) {
                        match = i[0];
                        mqMatch.addListener(render_responsive_bg);
                    } else {
                        mqMatch = false;
                    }
                } else {
                    match = i[0];
                }
            }
        });
        if (match) {
           // optional: convert to WebP 
           // The $lazy+webp extension provides the method $zwebp() that can convert image URLs to .webp.
            match = $zwebp(match);

            // render responsive background image
            el.style.backgroundImage = 'url(' + match + ')';

            // optional: apply requestAnimationFrame timing method from $async
            //$async.time('requestAnimationFrame', function() {
            //    el.style.backgroundImage = 'url(' + bg + ')';
            //});
        }
    }

    var mqMatch;
    var bg = el.getAttribute('rzbg');
    if (bg) {
        // parse responsive background image JSON
        bg = JSON.parse(bg);

        // render
        render_responsive_bg();

        // optional, sanitize HTML
        el.removeAttribute('data-rzbg');
    }
});
Clone this wiki locally