Web图片加载

2016-11-11  本文已影响0人  YangEric

Web端的性能优化很多都集中在对JavaScript和CSS上面,比如"Move Scripts to the Bottom","Put Stylesheets at the Top"等等,很多时候,我们的开发中会忽略掉,图片的优化其实也是非常重要的一部分。

我们关注JS和CSS的重点也是如何能够更快地下载图片。图片是用户可以直观看到的,他们并不会关注JS和CSS。

以最近做的一个页面同花顺理财周为例:

xhr img css/js doc 其他
Size(kb) 1.216 52.331 22 4.8 0
Time(ms) 453 1628 822 164 0

web端的图片加载

在开始本文的介绍之前,先了解一些基础的知识:
在网页中插入一张图片可以使用html的<img>标签[站外图片上传中……(2)],每插入新建一个<img>标签就会新建一个Image 对象。
Image 对象的属性 onload 声明了一个事件句柄函数,当图像装载完毕的时候就会调用这个句柄。
Image 对象的属性 onerror 声明了一个事件句柄函数,当装载图像的过程中发生了错误时就会调用这个句柄。

我们目前在页面中采取的两种加载方式

针对首屏的banner图

<!-- html -->
[站外图片上传中……(2)]

<!-- js -->
function load_img(url, url_s, o) {
    var img = new Image(); //创建一个Image对象,实现图片的预下载
    img.src = url;
    o.src = url_s;

    if (img.complete) { //图片已经被缓存,直接替换
        o.src = img.src;
        return;
    };

    img.onload = function() { //图片下载完毕时替换
        o.src = img.src;
    };
};

load_img(("images/header.png"), ("images/header.jpg"), document.getElementById("img_init"));

原理:用一张很小的图片A替换清晰banner图B优先加载,然后js新建源为图B的Image对象并监控其加载进度,当图B加载完成之后,再替换掉图A。
不足:

滚屏加载(图片的异步加载)

<!-- html -->
[站外图片上传中……(3)]

<!-- js -->
function timeOutLoad(container) {
    this.container = container || document;
    var imgs = [],_num = [];
    var oriImgs = this.container.getElementsByClassName('timeOutLoad');
    for (var i = 0; i < oriImgs.length; i++) {
        var _data = oriImgs[i].getAttribute("data-original");
        if (_data) {
            _num.push(i);
            imgs.push(_data);
        }
    }
    function loadByOne(i) {
        if (imgs.length > 0) {
            _img = new Image();
            _img.onload = function () {
                oriImgs[_num[i]].setAttribute('src', _img.src);
                oriImgs[_num[i]].removeAttribute('data-original');
                loadByOne(i + 1);
            };
            _img.src = imgs.shift();
        }
    }
    loadByOne(0);
}
timeOutLoad(document.getElementById("container"));
原理:

通过判断当图片元素是否出现在视窗范围内后,则去加载图片资源,否则不加载。

延伸

Litten说到加载图片,我们可以谈些什么

特殊状态处理

上报监控

webp

smaller、*richer *

一个较完善的可行方案

花落红尘 诺诶网络

// 更新:
// 05.27: 1、保证回调执行顺序:error > ready > load;2、回调函数this指向img本身
// 04-02: 1、增加图片完全加载后的回调 2、提高性能

/**
 * 图片头数据加载就绪事件 - 更快获取图片尺寸
 * @version    2011.05.27
 * @author    TangBin
 * @see        http://www.planeart.cn/?p=1121
 * @param    {String}    图片路径
 * @param    {Function}    尺寸就绪
 * @param    {Function}    加载完毕 (可选)
 * @param    {Function}    加载错误 (可选)
 * @example imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function () {
        alert('size ready: width=' + this.width + '; height=' + this.height);
    });
 */
var imgReady = (function () {
    var list = [], intervalId = null,

    // 用来执行队列
    tick = function () {
        var i = 0;
        for (; i < list.length; i++) {
            list[i].end ? list.splice(i--, 1) : list[i]();
        };
        !list.length && stop();
    },

    // 停止所有定时器队列
    stop = function () {
        clearInterval(intervalId);
        intervalId = null;
    };

    return function (url, ready, ,load error) {
        var onready, width, height, newWidth, newHeight,
            img = new Image();
        
        img.src = url;

        // 如果图片被缓存,则直接返回缓存数据
        if (img.complete) {
            ready.call(img);
            load && load.call(img);
            return;
        };
        
        width = img.width;
        height = img.height;
        
        // 加载错误后的事件
        img.onerror = function () {
            error && error.call(img);
            onready.end = true;
            img = img.onload = img.onerror = null;
        };
        
        // 图片尺寸就绪
        onready = function () {
            newWidth = img.width;
            newHeight = img.height;
            if (newWidth !== width || newHeight !== height ||
                // 如果图片已经在其他地方加载可使用面积检测
                newWidth * newHeight > 1024
            ) {
                ready.call(img);
                onready.end = true;
            };
        };
        onready();
        
        // 完全加载完毕的事件
        img.onload = function () {
            // onload在定时器时间差范围内可能比onready快
            // 这里进行检查并保证onready优先执行
            !onready.end && onready();
        
            load && load.call(img);
            
            // IE gif动画会循环执行onload,置空onload即可
            img = img.onload = img.onerror = null;
        };

        // 加入队列中定期执行
        if (!onready.end) {
            list.push(onready);
            // 无论何时只允许出现一个定时器,减少浏览器性能损耗
            if (intervalId === null) intervalId = setInterval(tick, 40);
        };
    };
})();

调用案例:

imgReady('http://www.google.com.hk/intl/zh-CN/images/logo_cn.png', function (){
    alert('size ready: width=' + this.width + '; height=' + this.height);
});
上一篇 下一篇

猜你喜欢

热点阅读