View on GitHub

essay

All about my technical essays and practicals

多图片页面另类加载方法

在页面上绘制canvas/webgl动画必须保证所需的序列帧顺利加载。最常见的做法是手动或者用loader把这些图片一张张从cdn加载进来,或者拼成精灵图,加载后手动分离序列帧。但是,单独加载会大幅增加http请求,某些api还要求同源策略,拼成精灵图又有图片过大风险。趁此机会,今天就向大家介绍几种不常见的,自己在项目中有到过的大批量图片加载方式。

背景介绍

利用Pixijs或Threejs制作视觉效果绚丽的h5宣传页越来越流行。哈利波特20周年UP2017年会等案例均有很好的传播效果。精美视觉效果意味着需要增加大量图片资源,而大批量图片加载一直是web页面优化中绕不过的点。无论是制作webgl/canvas动画,还是直接css轮播图片,开发人员都需要将设计师给的每一帧图片插入到页面,保证全部加载后再播放动画。

保证所需的大量图片顺利加载,最传统的做法是手动或者用动画引擎自带的loader把这些图片一张张从cdn load进来,或者拼成精灵图,加载后一张张分离。但是,单独加载会产生大量http请求,并要求cdn图片开启跨域支持,而拼成精灵图又有图片过大风险,导致低端移动设备闪退。

趁此机会,今天就向大家介绍几种不常见的,我在项目中用到过的大批量图片加载方式。

方法1:线下图片打包,页面直接下载后本地解压分离

得益于现代浏览器的Typed Array,bloburl,以及前人的努力,目前浏览器环境可以轻松下载二进制文件并在页面里解压得到里面的单个图片。只需用压缩软件将图片资源统一打包成zip格式,线上页面加载时请求这个zip文件,本地解压即可得到图片blob对象。该方法在热门文章《页面里全是swf文件?前端资源加载新思路!》中有非常详细的案例解析,非常值得一看。文章里提到的库是zlib,但我这里用jszip搭配jszip-utils做demo和分析。因为zlib的日文片假名文档实在太难看懂了。。。

可能存在的问题:

An other limitation comes from the browser (and the machine running the browser). A compressed zip file of 10MB is easily opened by firefox / chrome / opera / IE10+ but will crash older IE. Also keep in mind that strings in javascript are encoded in UTF-16 : a 10MB ascii text file will take 20MB of memory.

Performance issues

  • 编码问题:jszip只原生支持utf8编码,而且zip文件不会包含文件名的编码。所以如果文件名不是utf8编码,那么有可能出现文件名乱码等问题。

JSZip only supports UTF-8 natively. A zip file doesn’t contain the name of the encoding used, you need to know it before doing anything.

Encodings support

经典案例:

纪念碑谷2

方法2:转base64存到json中,请求后直接生成Image

把图片打包到zip里面无非是为了减少http请求,减少回调函数和代码复杂度,保证请求一次即可使用资源,而不必担忧单个图片加载出错的问题。按照这个思路,其实可以把序列帧按转成base64,按播放先后顺序放到json文件里。线上请求json文件,把base64填入image的src中得到序列帧。
就像这样:

$.ajax({
    url:'/sprites.json',
    dataType:'json',
    success:function(base64Ls){
        var imageLs = base64Ls.map(function(v){
            var img = new Image();
            img.src = v;  
            return img;
        });
        createSpriteAnimation(imageLs);
    }
});

推荐杭州技术中心实用工具图片转JSON工具进行批量图片转base64

可能存在的问题:

实际项目:

目前这个页面的动画资源就是用这种方法加载的,因为是直接绘制到canvas,所以也不处理dataurl跨域问题

方法3:图片转base64存js文件,放到cdn上用jsonp请求

如果仅仅加载大量图片,那么方法2是最简单的,但是会有各种跨域问题,所以又有方法3,图片转base64存js文件,取出后赋值src得到可用的image。用jsonp解决了跨域。于是变成下面这个样子: 资源:

jsonpCallback(['data:image/png;base64,各种地址1','data:image/png;base64,各种地址2','data:image/png;base64,各种地址2']);

获取:

$.ajax({
    url:'https://cdn.com/jsonp.js',
    dataType:'jsonp',
    callback:'jsonpCallback',
    success:function(base64Ls){
        var imageLs = base64Ls.map(function(v){
            var img = new Image();
            img.src = v;  
            return img;
        });
        createSpriteAnimation(imageLs);
    }
});

可能存在的问题:

实际项目:

与方法二一样

总结

对于web页面动画大量图片资源的加载,可以用这些方法:

三种图片加载方式各有优缺点,需要根据应用场合选择。方法二代码量和逻辑都很简单,如果只是简单地将序列帧图片绘制到canvas上,方法二非常合适。当项目中使用了webgl,图片不能跨域,而且有其他资源也想打包,那么推荐方法一,打包成zip,在线解包。 要是没办法把zip文件放到域名下,又没办法添加cors头部,那么就使用方法三吧,因为方法三没在实际中使用过,效果未知,在此不敢保证。

参考