js將用戶上傳gif動圖分解成多張幀圖片

js將用戶上傳gif動圖分解成多張幀圖片

寫在前面

工做中遇到一個這麼一個需求:這是一個多圖上傳的場景,若是用戶上傳選擇多張圖片,則上傳後直接展現多張圖片,若是上傳的圖片是gif動圖,則須要分解這張動圖拆分紅一幀一幀的單張圖片,再按順序展現出來。javascript

實現思路

這裏主要針對gif分解多圖的實現

  1. 首先對用戶上傳的文件格式進行判斷;
  2. 將gif動圖通過gif庫解析成gif實例
  3. 遍歷獲取gif實例的每一幀的canvas,轉換成baseURL,再轉一份file對象存放起來。
  4. 經過轉換後的baseURL展現到界面,用戶點上傳就把對應的file對象上傳服務器。

這裏最核心的就是2,3步,很是慶幸有https://github.com/buzzfeed/libgif-js 這個庫,才得以實現後面的步驟;java

代碼部分

因爲是公司項目就不展現界面和完整代碼,只放關鍵代碼:git

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 這個數組裏面。github

接下來只要拿img_list數組裏的file對象上傳到服務器便可。canvas

上傳方式各不相同,這裏就不放具體代碼了,須要注意的是,圖片上傳是異步操做,多圖上傳須要得知全部的圖片所有上傳成功才能肯定上傳完成,因此若是上傳的函數返回的是promise對象,則能夠直接用Promise.all函數便可得知全部圖片上傳完畢的回調。數組

相關文章
相關標籤/搜索