写在前面
工作中遇到一个这么一个需求:这是一个多图上传的场景,如果用户上传选择多张图片,则上传后直接展示多张图片,如果上传的图片是gif动图,则需要分解这张动图拆分成一帧一帧的单张图片,再按顺序展示出来。
实现思路
这里主要针对gif分解多图的实现
- 首先对用户上传的文件格式进行判断;
- 将gif动图经过gif库解析成gif实例
- 遍历获取gif实例的每一帧的canvas,转换成baseURL,再转一份file对象存放起来。
- 通过转换后的baseURL展示到界面,用户点上传就把对应的file对象上传服务器。
这里最核心的就是2,3步,非常庆幸有https://github.com/buzzfeed/libgif-js 这个库,才得以实现后面的步骤;
代码部分
由于是公司项目就不展示界面和完整代码,只放关键代码:
0. 引入gif库
1
| import { SuperGif } from './libgif.js'
|
1. 对用户上传的文件格式进行判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 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 = []; if (/(image\/gif)/.test(e.path[0].files[0].type)) { this.pre_load_gif(e.path[0].files[0]) return; }
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图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| 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}); },
convertCanvasToImage(canvas, filename) { return this.dataURLtoFile(canvas.toDataURL('image/png'), filename); }, pre_load_gif(gif_source) { var img_list = []; const gifImg = document.createElement('img'); gifImg.setAttribute('rel:animated_src', URL.createObjectURL(gif_source)) gifImg.setAttribute('rel:auto_play', '0') var rub = new SuperGif({ gif: gifImg } ); rub.load(() => { var img_list = []; for (let i=1; i <= rub.get_length(); i++) { rub.move_to(i); 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
函数即可得知所有图片上传完毕的回调。