當視頻能夠預覽並上傳後,非要來一張視頻第一幀的截圖貼上,第一幀是黑的怎麼辦,下一幀。
一、文件上傳
使用 <inputtype="file">
上傳, change
事件作爲預覽 video
的 src
的觸發條件
新鮮源碼:
<video controls width="700" height="300" src="" id="video"></video>
<input type="file" id="input" hidden />
<button id="fileBtn">點擊上傳視頻</button>
二、canvas截取圖片
關於截取或者處理圖片/視頻/富文本編輯器, canvas
是一個非常nice的選擇。
1.創建畫布 canvas
或在 html
中直接寫入。
var canvas = document.createElement('canvas');
2.創建基於 canvas
的繪圖環境
var ctx = canvas.getContext('2d');
附 Q&A:
什麼是繪圖環境?
網絡上的常規理解是“在準備畫布後,需要一些‘染料、畫筆、繪圖工具’的準備工作。”
比較官方的說話是返回 canvas
的上下文環境,
說人話是'你能夠更好的操作你的 canvas
'。
關於
getContext('2d')
的參數
方法中的 2d
參數目前可以理解爲是 固定參數
,表示想要一個 二維
繪製環境。雖然大家都認爲有 2d
自然應該有 3d
,然而實際上本身設計時也是這麼考慮的,不過大家有點等不起了,所以都去選擇 webGL
了。webGL
是啥?瀏覽器端藉助系統顯卡進行 3D 繪圖。這是另一個故事了( IE
別想了)。
關於
canvas.getContext('2d')
的返回值
返回一個 CanvasRenderingContext2D
對象,也就是上文所說的能夠支持絕大多數對畫布的操作。
3.在 canvas
上繪製圖片
// ctx.drawImage(file,sx,sy,swidth,sheight,x,y,width,height);
ctx.drawImage(this, 0, 0, swidth, sheight);
在不需要剪裁的情況下,使用上述參數即截取操作 file
的全部,繪製到 canvas
上
關於參數(w3school)
參數 | 描述 |
---|---|
file | 規定要使用的圖像、畫布或視頻。 |
sx | 可選。開始剪切的 x 座標位置。 |
sy | 可選。開始剪切的 y 座標位置。 |
swidth | 可選。被剪切圖像的寬度。 |
sheight | 可選。被剪切圖像的高度。 |
x | 在畫布上放置圖像的 x 座標位置。 |
y | 在畫布上放置圖像的 y 座標位置。 |
width | 可選。要使用的圖像的寬度。(伸展或縮小圖像) |
height | 可選。要使用的圖像的高度。(伸展或縮小圖像) |
4.將 canvas
導出成圖片放入 src
var src = canvas.toDataURL('image/jpeg');
關於 toDataURL()
方法。將 canvas
的內容導出
canvas.toDataURL(type, encoderOptions);
type
: 圖片格式,默認 image/jpeg
, encoderOptions
:圖片質量,取值範圍爲0到1,默認0.92。返回值
:包含 data URI
的 DOMString
,也就是 base64
格式。
三、截取視頻第一幀
上傳文件OK,用 canvas
截取OK,怎麼找 第一幀
呢?(啥時候開始截取呢?)
當然是多媒體的事件來觸發。關於 video
的事件非常多(全部事件),這裏只討論能夠影響到截取到第一幀的各個事件。
video.addEventListener('loadeddata', consoleString.bind(video, 'loadeddata')) // 當前幀加載完畢
video.addEventListener('loadedmetadata', consoleString.bind(video, 'loadedmetadata')) // 視頻元數據加載完畢
video.addEventListener('canplay', consoleString.bind(video, 'canplay')) // 視頻緩衝能夠開始播放
video.addEventListener('timeupdate', consoleString.bind(video, 'timeupdate')) // 播放位置發生改變時
video.addEventListener('play', consoleString.bind(video, 'play')) // 開始播放時
video.addEventListener('waiting', consoleString.bind(video, 'waiting')) // 要播放下一幀而需要緩衝時
function consoleString(string) {
console.log(string)
}
// 執行結果
// timeupdate
// loadedmetadata
// loadeddata
// canplay
// play(開始播放)
// 沒有waiting, 因爲視頻較小不需要緩衝
根據順序,第一個被觸發的竟然是
timeupdate
事件,按設想來說,最先執行的應該是loadedmetadata
,元數據加載完畢。關於這一點,在MDN上沒有明確的說明,但是可以推理一下:
當
currentTime
更新時會觸發timeupdate
事件
來源:MDN
loadedmetadata
的元數據恰好是指 時長、尺寸(僅視頻)以及文本軌道
,也就是說在 video
未定義的時候 currentTime
是 NaN
或 NULL
,當元數據中時長加載完畢後, currentTime
更新至 0
,因此觸發。
結論:雖然最先觸發,但是此時視頻文件尚未加載,截取的是 canvas
的無內容本身。注:timeupdate
事件根據使用的系統不同,每秒觸發4-66次,且由於觸發頻率高,單位過小(毫秒級別),事件響應需要延遲等原因,無法完全精準的控制。
loadedmetadata 上文提到,元數據加載完畢之後即觸發,但數據中並不包括視頻文件本身。結論:如果視頻文件較大,加載時間較長,仍然無法截取到已加載的第一幀。補充:通過
URL.createObjectURL()
方法能夠基本做到無察覺,但並不保險。loadeddata
當前幀數(第一幀)加載完畢觸發,沒毛病。結論:可用。補充:萬一第一幀是黑屏想用下一幀怎麼辦,對不起,餘下幀數加沒加載完不在它的考慮範圍之類,這個事件不管。
canplay
視頻能夠開始播放時觸發,也就是根據上傳的視頻幀數決定加載多少幀(24/25/30/60等等)後滿足播放畫面後觸發。總結:因爲加載相對於
loadeddata
的事件來說更多(多一丟丟),總體可行。補充:通過控制currentTime
可以滿足(但不可能是第二幀那麼準確),可以看做“當前播放幀”。play
開始播放時纔會觸發,和上傳快速截取的需求不是很符合。
waiting
已播放但下一畫面沒緩衝好時觸發,適合插播小廣告。
文件、方法、事件都OK了。截就完事兒了。
video.addEventListener('loadeddata', function (e) {
canvas.width = this.videoWidth
canvas.height = this.videoHeight
width = this.videoWidth
height = this.videoHeight
ctx.drawImage(this, 0, 0, width, height);
var src = canvas.toDataURL('image/jpeg');
img.src = src;
// var currentTime = this.currentTime
// duration = this.duration
// var fps = duration / 30
})
Dome:https://243341386.github.io/review_dome/