js将用户上传gif动图分解成多张帧图片

js将用户上传gif动图分解成多张帧图片

写在前面

工作中遇到一个这么一个需求:这是一个多图上传的场景,如果用户上传选择多张图片,则上传后直接展示多张图片,如果上传的图片是gif动图,则需要分解这张动图拆分成一帧一帧的单张图片,再按顺序展示出来。

实现思路

这里主要针对gif分解多图的实现

  1. 首先对用户上传的文件格式进行判断;
  2. 将gif动图经过gif库解析成gif实例
  3. 遍历获取gif实例的每一帧的canvas,转换成baseURL,再转一份file对象存放起来。
  4. 通过转换后的baseURL展示到界面,用户点上传就把对应的file对象上传服务器。

这里最核心的就是2,3步,非常庆幸有https://github.com/buzzfeed/libgif-js 这个库,才得以实现后面的步骤;

代码部分

由于是公司项目就不展示界面和完整代码,只放关键代码:

0. 引入gif库

import { SuperGif } from  './libgif.js'

1. 对用户上传的文件格式进行判断

// 判断文件格式并展示的函数
pre_upload() {
    // 点击上传按钮触发弹出文件选择框
    const input = document.createElement('input');
    input.setAttribute('type', 'file');
    // 注意要设置多选属性
    input.setAttribute('multiple', 'true');
    input.addEventListener('change', (e) => {
        this.img_list = [];
        this.can_upload = true;
        this.qiniu_url_list = [];
        // 判断是gif格式则交给this.pre_load_gif函数处理
        if (/(image\/gif)/.test(e.path[0].files[0].type)) {
            this.pre_load_gif(e.path[0].files[0])
            return;
        }

        // 如果是上传多张静态的png、jpg图片则直接转换成baseURL
        var img_list = [];
        for(let i=0,item; item = e.path[0].files[i]; i++) {
            if (!/(image\/png)|(image\/jp(e?)g)/.test(item.type)) {
                alert('请上传jpg、png格式的图片')
                return;
            }
            img_list.push({
                file_name: item.name,
                url: URL.createObjectURL(item),
                file: item,
            })
        }
        this.img_list = img_list
    });
    input.click();
},

2. 分解gif图片

dataURLtoFile(dataurl, filename) {
    const arr = dataurl.split(',');
    const mime = arr[0].match(/:(.*?);/)[1];
    const bstr = atob(arr[1]);
    var n = bstr.length;
    const u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, {type:mime});
},
// 将canvas转换成file对象
convertCanvasToImage(canvas, filename) {
    return this.dataURLtoFile(canvas.toDataURL('image/png'), filename);
},
pre_load_gif(gif_source) {
    var img_list = [];
    const gifImg = document.createElement('img');
    // gif库需要img标签配置下面两个属性
    gifImg.setAttribute('rel:animated_src', URL.createObjectURL(gif_source))
    gifImg.setAttribute('rel:auto_play', '0')
    // 新建gif实例
    var rub = new SuperGif({ gif: gifImg } );
    rub.load(() => {
        var img_list = [];
        for (let i=1; i <= rub.get_length(); i++) {
            // 遍历gif实例的每一帧
            rub.move_to(i);
            // 将每一帧的canvas转换成file对象
            let cur_file = this.convertCanvasToImage(rub.get_canvas(), gif_source.name.replace('.gif', '') + `-${i}`)
            img_list.push({
                file_name: cur_file.name,
                url: URL.createObjectURL(cur_file),
                file: cur_file,
            })
        }
        this.img_list = img_list
    });
},

至此,核心功能基本实现,上面的函数已经将gif分解成多张图片存放在this.img_list 这个数组里面。

接下来只要拿img_list数组里的file对象上传到服务器即可。

上传方式各不相同,这里就不放具体代码了,需要注意的是,图片上传是异步操作,多图上传需要得知所有的图片全部上传成功才能确定上传完成,所以如果上传的函数返回的是promise对象,则可以直接用Promise.all函数即可得知所有图片上传完毕的回调。

posted @ 2018-08-27 17:07  lipten  阅读(1361)  评论(0编辑  收藏  举报