需求說明
前端實現視頻截取片段,并轉換為gif圖片下載到本地
參考文檔
HTML5 CSS3 誘人的實例 :模仿優酷視頻截圖功能
github antimatter15
結果說明
純前端通過canvas截幀并可以保存為gif形式當前在移動端chrome、safari瀏覽器支持較好。
demo展示
canvas截圖圖片實例
- 截取圖片
在截取視頻之前,首先進行了截取圖片的嘗試。截取圖片或者視頻主要使用了canvas的CanvasRenderingContext2D.drawImage()
MDN說明該函數可以在canvas畫布上繪制圖像,繪制圖像后通過HTMLCanvasElement.toDataURL()
函數返回一個包含圖片展示的dataUrlMDN說明
this.canvas = document.createElement("canvas”);
this.ctx = this.canvas.getContext("2d”);
this.ctx.drawImage( this.img,0,0,this.Width, this.Height,0,0,this.Width,this.Height);
let dataUrl = this.canvas.toDataURL("image/png”);
- 轉成gif下載
轉成gif下載主要使用了jsgif
中的如下代碼,引入了 如下JS文件
<script type="text/javascript" src="LZWEncoder.js"></script>
<script type="text/javascript" src="NeuQuant.js"></script>
<script type="text/javascript" src="GIFEncoder.js"></script>
<script type="text/javascript" src="b64.js"></script>
這里需要注意的是
encoder.addFrame(context);
中傳遞的context參數,文檔中說或者是canvas元素,或者是imageData形式。那么imageData.data類型要怎么獲取到呢?通過函數CanvasRenderingContext2D.getImageData()
MDN說明,該函數可以返回一個imageDataMDN說明對象,ImageData.data是[Uint8ClampedArray
]類型,描述了一個一維數組,包含以 RGBA 順序的數據,數據使用(包含)的整數表示。將ImageData.data傳入encoder.addFrame即可,這樣就實現了canvas截圖后并將其轉成gif形式。
let context = this.ctx.getImageData(0,0,this.Width,this.Height).data
var encoder = new GIFEncoder();
encoder.setRepeat(0); //0 -> loop forever
//1+ -> loop n times then stop
encoder.setDelay(10); //這里單張圖片并不需要用到,用于設置幀數間隔時間
encoder.start();
encoder.setSize(640, 430); //此處應和canvas獲得的圖片尺寸一致
encoder.addFrame(context, true);
encoder.finish();
encoder.download("download.gif");
- gif下載
單張圖片是完全沒有必要下載成gif的,這個主要是為了視頻截取而準備,在GIFEncoder.js中,主要采用如下代碼實現了圖片下載。
ar templink = document.createElement("a");
templink.download=filename;//filename = ‘download.gif'
templink.href= URL.createObjectURL(new Blob([new Uint8Array(out.bin)], {type : "image/gif" } ))
console.log(templink)
templink.click()
在上面代碼中templink得到的是如下一個超鏈接標簽,在chrome中執行click()會直接下載該文件,safari則會打開一個新的頁面
<a download="download.gif" href="blob:http://xxxxxx:3023/12465504-7074-466a-8828-29ee44848612"></a>
上面創建下載鏈接主要是采用了URL.createObjectURL()
對象MDN說明
MDN上說該靜態方法會創建一個 [DOMString
]其中包含一個表示參數中給出的對象的URL。這個 URL 的生命周期和創建它的窗口中的 [document
]綁定。這個新的URL 對象表示指定[File
]對象或 [Blob
]對象
Blob對象表示不可變的類似文件對象的原始數據。Blob表示不一定是JavaScript原生形式的數據。 File 接口基于Blob,繼承了 blob的功能并將其擴展使其支持用戶系統上的文件對象
這里對Blob的理解并不深刻,上面獲取的href地址從測試結果來看,uc、QQ等瀏覽器是不支持其打開預覽下載的。所以這個合成gif并下載的功能在chrome和safari可支持使用。
canvas截取視頻實例
截取視頻和截取圖片的區別在于視頻是多次截取拼成gif圖片,在截圖、轉成GIF、下載與截取圖片所用核心代碼基本一致,差別只在于視頻需要設置起始于結束時間,該時間段內setInterval循環繪制canvas圖片,循環繪制后,生成imageData.data的數組,該數組循環調用encoder.addFrame(context, true)
方法。
事件主要代碼如下:
var draw = new drawVideo($('video')[0]);
document.getElementById('j_begin').addEventListener('click',()=>{
document.getElementById('video').currentTime = startTime;
document.getElementById('video').play();
var time = setInterval(()=>{
draw.paint();
document.getElementById('video').play(); //每次繪制完需要將視頻播放
if(document.getElementById('video').currentTime > endTime){//超過當前結束時間停止繪制canvas
console.log('stop');
clearInterval(time);
var encoder = new GIFEncoder();
encoder.setRepeat(0); //0 -> loop forever
//1+ -> loop n times then stop
encoder.setDelay(300); //這個時間的設置會影響gif展現時的速度
encoder.start();
encoder.setSize(1280, 720);//此處高度應為視頻寬高
draw.result.forEach((ele,index)=>{
encoder.addFrame(ele, true);//循環添加canvas的每一幀合成gif
})
encoder.finish();
encoder.download("download.gif");
}
},1500);//1500秒為循環時間,如果時間設置太短看,手機上卡頓特別明顯,時間設置太長,生產的gif不流暢。
},false)