美文网首页前端路前端开发那些事程序员
图片延迟加载策略(JavaScript)

图片延迟加载策略(JavaScript)

作者: iceman_dev | 来源:发表于2017-03-15 15:51 被阅读296次

    不管是在移动端开发还是在PC端开发,基本都会用到图片的懒加载,这其中最大的原因就是为了保证页面打开的速度(正常情况下3秒之内如果首页打不开,就已经算是死亡时间了)。

    延迟加载的的基本做法一般是这样的:

    • 首屏内容中的图片:首先给对应的区域一张默认的图片占着位置(默认图需要非常的小,一般可以维持在5kb以内),当首屏内容都加载完成后(或者也可以给一个延迟的时间),再开始加载真实的图片。

    • 其他屏中的图片:也是给一张默认的图片占位,当滚动条滚动到对应区域的时候,我们再开始加载真实的图片。

    扩展:对于数据的异步加载,也是这样的,并不会一次性把页面中所有的数据都请求出来,正常情况是先请求前两屏的数据,后面的数据先不请求,当页面滚动到对应区域的时候,再重新请求数据,接着绑定渲染数据。

    一、首屏单张图片延迟加载

    html

    <div id="banner">
        <!--trueImg是当前IMG标签的自定义属性,存储的是真实图片的地址-->
        <img src="" trueImg="img/jd.jpg">
    </div>
    

    css

    * {margin: 0; padding: 0; font-family: "\5FAE\8F6F\96C5\9ED1", Helvetica, sans-serif; font-size: 14px;}
    img {display: block;border: none;}
    #banner {margin: 10px auto; width: 350px; height: 200px; border: 1px solid green; background: url("img/default.gif") no-repeat center center #e1e1e1; }
    #banner img {display: none; width: 100%;height: 100%;}
    

    在设置样式的时候,把默认的图片设置给img外层的div,作为div的背景,因为要延迟加载,所以一开始给src的属性值是空,这样的话在IE浏览器当中会显示一张碎图,不美观,所以我们让其默认是隐藏的,当真实的图片加载完成后,再显示。

    JavaScript
    在没有想其他情况的时候,我们可能会很简单的就把JavaScript代码写成以下这样:

    var banner = document.getElementById('banner') , imgFir = banner.getElementsByTagName('img')[0];
    
    imgFir.src = imgFir.getAttribute('trueImg');
    imgFir.style.display = 'block';
    

    但是以上处理是不完整的:如果我们获取的真实图片地址是错误的,当赋值给IMG的src属性的时候,不仅控制台会报错,而且页面中会出现碎图/叉子图,影响视觉效果。所以我们获取图片的地址后,应该先验证地址的有效性,是有效的才赋值,不是有效的是不进行赋值处理的。

    var oImg = new Image; // 创建一个临时的img标签
    oImg.src = imgFir.getAttribute('trueImg');
    oImg.onload = function () { // 当图片能够正常加载
        imgFir.src = this.src;
        imgFir.style.display = 'block';
        oImg = null;
    }
    

    二、超过一屏的单张图片延迟加载

    超过一屏的单张图片.png

    A:图片容器距离body的上偏移 + 容器的offsetHeight

    B:浏览器一屏幕的高度 + 滚动条卷去的高度

    所以判断的条件就是:A < B 容器就已经完全的进入了视野,加载真实的图片。

    window.onscroll = function () {
        if (banner.isLoad) { // 已经加载过了
            return;
        }
        var A = banner.offsetHeight + utils.offset(banner).top;
        var B = utils.win('clientHeight') + utils.win('scrollTop');
        if (A < B) {
            console.log('ok');
            // 当条件成立,加载真实的图片,第一次加载完成后,再让页面继续滚动的过程中,A<B一直成立,又重新的执行了下面的操作,导致了重复给一个容器中的图片进行加载,所以有下面的isLoad自定义属性
            var oImg = new Image;
            oImg.src = imgFir.getAttribute('trueImg');
            oImg.onload = function () {
                imgFir.src = this.src;
                imgFir.style.display = 'block';
                oImg = null;
            };
            // 设置一个自定义属性,告诉浏览器我已经把图片加载完了(不管是否正常的加载,只要处理过一次以后都不再处理了)
            banner.isLoad = true;
        }
    }
    

    本例中用到的utils工具类中的offset方法和win方法:

    function offset(curEle) {
        var disLeft = curEle.offsetLeft, disTop = curEle.offsetTop, par = curEle.offsetParent;
        while (par) {
            if (navigator.userAgent.indexOf("MSIE 8") === -1) {
                disLeft += par.clientLeft;
                disTop += par.clientTop;
            }
            disLeft += par.offsetLeft;
            disTop += par.offsetTop;
            par = par.offsetParent;
        }
        return {left: disLeft, top: disTop};
    }
    
    function win(attr, value) {
        if (typeof value === "undefined") {
            return document.documentElement[attr] || document.body[attr];
        }
        document.documentElement[attr] = value;
        document.body[attr] = value;
    }
    

    多张图片延迟加载

    本例为移动端的情况,有一个列表,超过屏幕好几屏

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>03-多张图片的延迟加载</title>
        <!--做移动端响应式布局页面,我们必须要加META:viewport-->
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
        <style type="text/css">
            * {margin: 0; padding: 0; font-family: "\5FAE\8F6F\96C5\9ED1", Helvetica, sans-serif; font-size: 14px;}
            ul,li {
                list-style: none;
            }
            img {
                display: block;
                border: none;
            }
            /* 最外层的容器是不设定固定宽高的 */
            .news {
                padding: 10px;
            }
            
            .news li {
                position: relative;
                height: 60px;
                padding: 10px 0;
                border-bottom: 1px dashed #ccc;
            }
            .news li > div:nth-child(1) {
                position: absolute;
                top: 10px;
                left: 0;
                width: 75px;
                height: 60px;
                background: url("img/default.jpg") no-repeat center center;
                background-size: 100% 100%;
            }
            .news li > div:nth-child(1) img {
                width: 100%;
                height: 100%;
                display: none;
            }
            .news li > div:nth-child(2) {
                margin-left: 80px;
                height: 60px;
            }
            .news li > div:nth-child(2) h2 {
                height: 20px;
                line-height: 20px;
                /* 实现文字超出一行,自动裁切 */
                overflow: hidden;
                text-overflow: ellipsis; /*裁下去的步伐以三个省略号展示*/
                white-space: nowrap;
            }
            .news li > div:nth-child(2) p {
                height: 20px;
                line-height: 20px;
                font-size: 12px;
                color: darkgrey;
            }
        </style>
    </head>
    <body>
    
    <ul class="news" id="news">
        <!--<li>-->
            <!--<div>-->
                <!--<img src="" trueImg="img/1.jpg" alt="">-->
            <!--</div>-->
            <!--<div>-->
                <!--<h2>AAAAAAAAAAAAAAAAAAAAAAAAAAAAA</h2>-->
                <!--<p>BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB</p>-->
            <!--</div>-->
        <!--</li>-->
    </ul>
    
    
    <script type="text/javascript" src="js/utils.js"></script>
    <script type="text/javascript">
        var news = document.getElementById("news"), imgList = news.getElementsByTagName("img");
        
        // 1、获取需要绑定的数据(Ajax)->json/newsList.txt中的JSON格式的字符串
        var jsonData = null;
        ~ function () {
            var xhr = new XMLHttpRequest();
            //URL地址后面加的随机数是在清除每一次请求数据时候(GET请求)产生的缓存
            xhr.open("get", "json/newsList.txt?_=" + Math.random(), false);
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
                    var val = xhr.responseText;
                    jsonData = utils.formatJSON(val);
                }
            };
            xhr.send(null);
        }();
        console.log(jsonData);
    
        //2、数据绑定->把jsonData中存储的数据绑定在页面中(字符串拼接)
        ~function () {
            var str = '';
            if (jsonData) {
                for (var i = 0, len = jsonData.length; i < len; i++) {
                    var curData = jsonData[i];
                    str += '<li>';
                    str += '<div><img src="" trueImg="' + curData["img"] + '"/></div>';
                    str += '<div>';
                    str += '<h2>' + curData["title"] + '</h2>';
                    str += '<p>' + curData["desc"] + '</p>';
                    str += '</div>';
                    str += '</li>';
                }
            }
            news.innerHTML = str;
        }();
        
        // 3、图片延迟加载
        //->我先编写一个方法,实现单张图片的延迟加载
        function lazyImg(curImg) {
            console.log('ok')
            var oImg = new Image;
            oImg.src = curImg.getAttribute("trueImg");
            oImg.onload = function () {
                curImg.src = this.src;
                curImg.style.display = "block";
                fadeIn(curImg);
                oImg = null;
            };
            curImg.isLoad = true;
        }
        
        //->实现渐现的效果
        function fadeIn(curImg) {
            var duration = 500, interval = 10, target = 1;
            var step = (target / duration) * interval;
            var timer = window.setInterval(function () {
                var curOp = utils.getCss(curImg, "opacity");
                if (curOp >= 1) {
                    curImg.style.opacity = 1;
                    window.clearInterval(timer);
                    return;
                }
                curOp += step;
                curImg.style.opacity = curOp;
            }, interval);
        }
    
        //->循环处理每一张图片
        function handleAllImg() {
            for (var i = 0, len = imgList.length; i < len; i++) {
                var curImg = imgList[i];
    
                //->当前的图片处理过的话,就不需要在重新的进行处理了
                if (curImg.isLoad) {
                    continue;
                }
    
                //->只有A<B的时候在进行处理:当前图片是隐藏的,我们计算的A的值其实是计算它父亲(容器)的值
                var curImgPar = curImg.parentNode;
                var A = utils.offset(curImgPar).top + curImgPar.offsetHeight, B = utils.win("clientHeight") + utils.win("scrollTop");
                if (A < B) {
                    lazyImg(curImg);
                }
            }
        }
    
        //4、开始的时候(过500ms加载第一屏幕的图片)、滚动条滚动的时候加载其它图片
        window.setTimeout(handleAllImg, 500);
        window.onscroll = handleAllImg;
    </script>
    </body>
    </html>
    

    个人公众号(icemanFE):分享更多的前端技术和生活感悟

    个人公众号.png

    相关文章

      网友评论

        本文标题:图片延迟加载策略(JavaScript)

        本文链接:https://www.haomeiwen.com/subject/ugyugttx.html