應用canvas獲取video的縮略圖

發佈視頻時候,常常須要上傳同時上傳視頻的縮略圖,近日,應產品經理的的要求,須要作一個發佈視頻動態的功能,我第一反應就想到了H5的標籤vidio和canvas,在這裏記錄下我完成該功能的過程:
首先,總體思路是建立一個vidio,而後建立一個canvas和一個畫筆,調用畫筆的drawImage方法,將vidio做爲參數,就會畫出該視頻的縮略圖。ajax

二話不說擼代碼:json

function creatImg() {
  const video = document.getElementById('videoPreview');
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const imgHeight = video.videoHeight;
  const imgWidth = video.videoWidth;
  ctx.drawImage(video, 0, 0, imgWidth, imgHeight);

  const imgSrc = canvas.toDataURL('image/png');

  console.log(imgSrc);
}

該方法是在上傳視頻發送ajax成功的回調函數中調用的,canvas

// 上傳視頻
  $('#video').on('change', (e) => {
    const video = e.currentTarget.files[0];
    const formData = new FormData();
    formData.append('file', video);
    
    $.ajax({
      url: videourl,
      crossDomain: true,
      data: formData,
      dataType: 'json',
      method: 'POST',
      contentType: false,
      processData: false,
      timeout: 0,
    }).done((jsonData) => {
      //設置video標籤的src操做
      setTimeout(() => {
        creatImg();
      }, 500);
    }).fail(() => {
      layer.alert('上傳失敗', { shift: 5 });
    })
  });

這樣就完成這個功能了......開玩笑,怎麼能讓你好過?運行後報了以下bug:跨域

圖片描述

這個bug的意思是:髒的canvas不會被引入,出現這個問題的緣由是canvas中引用的視頻對象跨域了,從而污染了canvas,解決問題的方法是在canvas中引入本地的視頻對象,其實作法很簡單,改變一下creatImage引用的位置,在類型爲file的input標籤數組中取到video對象,這裏的video對象是本地的,從而不會出現跨域的現象,以下面代碼所示:數組

$('#video').on('change', (e) => {
    const video = e.currentTarget.files[0];
    creatImg(video);

    // const videoName = video.name;
    const formData = new FormData();
    formData.append('file', video);

    const index = layer.load(2);

    $.ajax({
      url: `${upload_url}/toServer`,
      crossDomain: true,
      data: formData,
      dataType: 'json',
      method: 'POST',
      contentType: false,
      processData: false,
      timeout: 0,
    }).done((jsonData) => {
      //這是video的src屬性值的相關操做
    }).fail(() => {
      layer.alert('上傳失敗', { shift: 5 });
    })
  });

const video = e.currentTarget.files[0];這裏的video是本地的video對象,接下來,咱們優化一下creatImg函數,以下代碼所示:服務器

function creatImg(stream) {
  const video = document.createElement('video');

  video.addEventListener('loadedmetadata', function loadedmetadata() {
    setTimeout(() => {
      const canvas = document.createElement('canvas');
      canvas.width = this.videoWidth;
      canvas.height = this.videoHeight;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(this, 0, 0);

      const image = new Image();
      image.src = canvas.toDataURL('image/png');
      image.onload(() => {
        const formData = new FormData();
        formData.append('file', dataURItoBlob(canvas.toDataURL('image/png')), `${+new Date()}.png`);

        $.ajax({
          url: upload_url,
          crossDomain: true,
          data: formData,
          dataType: 'json',
          method: 'POST',
          contentType: false,
          processData: false,
        }).done((jsonData) => {
          //.....
          //將值存儲在id爲thumbnail的input標籤當中的value屬性當中
          const body = jsonData.body;
          $('#thumbnail').val(body);
        }).fail(() => {
          layer.alert('上傳失敗');
        });
      });
    }, 300);
  }, false);

  video.src = URL.createObjectURL(stream);
  video.play();
}

建立canvas,建立畫筆,將video對象傳入畫出圖片,這一系列操做都是在video加載以後完成的,因此,全部操做都在video監聽的loademetadata的回調函數當中完成的,在圖片畫好之後須要將該圖片上傳到服務器,經過FormData對象來生成用於ajax向服務器發送的name:value的鍵值對,在給FormData添加鍵值對是調用append方法,append方法的第一個參數字段名,第二個參數是字段值,這裏的字段值是一個Blob對象,第三個參數是文件名稱,這裏建立Blob的方法是 dataURItoBlob方法,以下面代碼所示,由於canvas.toDataURL的方法返回的是圖片的Base64的編碼,因此這個函數的做用就是將Base64的編碼轉換成Blob對象,具體過程如代碼所示:app

function dataURItoBlob(dataURI) {
  const binary = atob(dataURI.split(',')[1]);
  const array = [];
  for (let i = 0; i < binary.length; i += 1) {
    array.push(binary.charCodeAt(i));
  }
  return new Blob([new Uint8Array(array)], { type: 'image/png' });
}

這裏首先調用window的atob的方法,將被編譯成的Base64的數據解碼成二進制的數據,接下來調用binary對象的charCodeAt方法來將制定位置上的二進制數據進行unicode編碼,並把全部的編碼後的數據存儲在數組當中,最後根據這個數組建立一個Unit8Array的對象,該對象是無符號8位整數,由於初始化Blob時的第一個參數必須是數組,因此要把這個8位無符號整數存放在數組當中。ide

總結:在完成這個功能的過程當中涉及道幾個重要的知識點,羅列在下面,我將在後面的相關文章當中逐一具體介紹:
一、canvas的video必須是同域的,若是video跨域了,那麼將會污染canvas
二、在向服務器發送圖片的時候,須要用到幾個重要的對象和方法函數

  • FormData對象:可讓XMLHttpRequest發送鍵值對組成的對象優化

  • window.atob方法:將Base64的數據解碼成二進制的數據

  • binary.charCodeAt方法:將二進制的數據進行unicode編碼

  • Uint8Array對象:經過傳入的數組建立一個無符號的八位整數

  • Blob對象:不可變的,原始數據的相似文件對象

相關文章
相關標籤/搜索