Lazy loading of pictures and exception handling

Posted by inaba on Mon, 03 Jun 2019 18:33:52 +0200

Picture display is often involved in daily page development.Sometimes when there are too many picture resources, we want to delay the loading of pictures, and when the loading fails, we can use a default picture to display them instead.The effect is shown in the diagram:

1. Lazy loading of pictures

In fact, the core idea of lazy loading pictures is very simple:

  1. Record the actual picture address by pointing the SRC resource of the picture to a small picture or an empty one beforehand and using data-src.
  2. Lazy loading of pictures is achieved by delaying loading or listening for scrolling events (pictures appear in the viewable area) and assigning data-src attribute values to srcs.
setTimeout({
    $imgs.each(function () {
        var $img = $(this);
        $img.src = $img.attr('data-src');
    });  
}, 0);

// Set up 500ms anti-jitter to avoid callbacks executing frequently and affect performance
$(window).on('scroll', _.debounce(function () {
    var $window = $(window); 
    var top = parseInt($window.height()) + parseInt($window.scrollTop());
    $imgs.each(function () {
        var $img = $(this);
        var oSrc = $img.attr('data-src');
        if (!oSrc) return;
        if ($img.offset().top < top) {
            $img.src = oSrc;
            $img.attr('data-src', '');
        }
}, 500));

2. Error handling of picture loading

Sometimes a picture resource request fails because of a network request or a resource problem, and the picture is displayed as a very ugly effect (broken picture).This can be handled by listening for the onerror time of the picture.

$img.onerror = function () {
    $img.src = 'default.jpg';
}

To avoid the infinite execution of picture resources when default pictures also fail to load, a one-time onerror event can be bound using jQuery's one() api interface.

$img.one('error', function () {
    $img.src = 'default.jpg';
});

tip: When loading pictures, we can load the picture resources by creating an Img element that is not in the rendering tree.

var oSrc = $img.attr('data-src');
var img = document.createElement('img');
img.onload = function () {
    $img.src = oSrc;
};
img.onerror = function () {
    console.debug('Picture loading failed:', img.src);
    // At this point, $img src is still the default image, and if the image replacement judges not data-src, no emptying is possible
    $img.attr('data-src', '');
};
img.src = oSrc;

3. Use background-size attribute instead of img tag

Most of the time, the width and height of the picture is fixed.However, the actual width and height of the picture are unknown. The width and height of the picture can be set in the following ways:

  1. Setting the width and height of the IMG directly to img {width: 100%; height: 100%;} can cause severe picture distortion.
  2. By setting the maximum width and height, img {max-width: 100%; max-height: 100%;} will have white space up, down, or around, and more when the width and height of the resource picture is smaller than the container.

background-size: cover; maximizes picture utilization without white space and picture distortion.However, there will be a loss of picture resources (pictures cannot be fully displayed).

.img {
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center;
}

4. Implement a Picture Processing Instruction with Vue

export default function (el, binding) {
    setTimeout(() => {
        const img = document.createElement('img');
        img.onload = function () {
            el.style.backgroundImage = `url(${binding.value})`;
        };
        img.onerror = function () {
            console.debug('Picture loading failed:', img.src);
        };
        img.src = binding.value;
    }, 0);
};

Full sample code:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
    <title>Picture Lazy Load Processing</title>
    <style>
        .img-group {
            display: flex;
            width: 800px;
            flex-wrap: wrap;
            list-style-type: none;
        }
        .img-item {
            flex: 0 0 33.3%;
            height: 100px;
            background-color: aquamarine;
            background-position: center;
            background-repeat: no-repeat;
            background-size: cover;
        }
        .img-item:nth-child(odd) {
            background-color: chocolate;
        }
    </style>
</head>

<body>
    <div id="app"></div>

    <template id="tpl">
        <ul class="img-group">
            <li class="img-item" v-for="img in imgs" v-img=" img"></li>
        </ul>
    </template>

    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
    new Vue({
        el: '#app',
        template: '#tpl',
        data() {
            return {
                imgs: [
                    'http://inews.gtimg.com/newsapp_match/0/9167593089/0',
                    'http://inews.gtimg.com/newsapp_match/0/9167593090/0',
                    'http://inews.gtimg.com/newsapp_match/0/9167593092/0',
                    'http://inews.gtimg.com/newsapp_match/0/9167593093/0',
                    'http://inews.gtimg.com/newsapp_match/0/9167593095/0',
                    'http://inews.gtimg.com/newsapp_match/0/9167595616/0',
                    'http://inews.gtimg.com/newsapp_match/0/9167595617/0',
                    'http://inews.gtimg.com/newsapp_match/0/9167595618/0',
                    'http://inews.gtimg.com/newsapp_match/0/9167595619/0'
                ],
            };
        },
        directives: {
            img(el, binding) {
                setTimeout(() => {
                    const img = document.createElement('img');
                    img.onload = function() {
                        el.style.backgroundImage = `url(${binding.value})`;
                    };
                    img.onerror = function() {
                        console.debug('Picture loading failed:', img.src);
                    };
                    img.src = binding.value;
                }, 0);
            },
        },
    });
    </script>
</body>

</html>

5. Picture optimization

If it is a front-end resource picture, you can also make some other optimizations for the picture:

  • Compress pictures to reduce their size ( Smart Map: A Picture Optimization Platform)
  • Responsive request for picture resources (HD screen request @2x pictures, general screen request @x pictures, control picture size, reduce picture size)
  • Reduce picture http requests (Sprite)
  • Use font libraries instead of icons (canvas, svg drawing instead of icons)

Detailed instructions can be referred to as: Picture optimization for web front-end optimization

Topics: Javascript Vue Attribute network JQuery