当视频可以预览并上传后,非要来一张视频第一帧的截图贴上,第一帧是黑的怎么办,下一帧。html5
使用<input type="file">
上传,change
事件做为预览video
的src
的触发条件 新鲜源码:git
<video controls width="700" height="300" src="" id="video"></video>
<input type="file" id="input" hidden />
<button id="fileBtn">点击上传视频</button>
复制代码
关于截取或者处理图片/视频/富文本编辑器,canvas
是一个很是nice的选择。github
canvas
或在html
中直接写入。var canvas = document.createElement('canvas');
复制代码
canvas
的绘图环境var ctx = canvas.getContext('2d');
复制代码
附 Q&A:web
网络上的常规理解是“在准备画布后,须要一些‘染料、画笔、绘图工具’的准备工做。” 比较官方的说话是返回canvas
的上下文环境, 说人话是'你可以更好的操做你的canvas
'。canvas
getContext('2d')
的参数方法中的2d
参数目前能够理解为是固定参数
,表示想要一个二维
绘制环境。虽然你们都认为有2d
天然应该有3d
,然而实际上自己设计时也是这么考虑的,不过你们有点等不起了,因此都去选择webGL
了。 webGL
是啥?浏览器端借助系统显卡进行 3D 绘图。这是另外一个故事了(IE
别想了)。浏览器
canvas.getContext('2d')
的返回值返回一个CanvasRenderingContext2D
对象,也就是上文所说的可以支持绝大多数对画布的操做。bash
canvas
上绘制图片// ctx.drawImage(file,sx,sy,swidth,sheight,x,y,width,height);
ctx.drawImage(this, 0, 0, swidth, sheight);
复制代码
在不须要剪裁的状况下,使用上述参数即截取操做file
的所有,绘制到canvas
上网络
关于参数(w3school)dom
参数 | 描述 |
---|---|
file | 规定要使用的图像、画布或视频。 |
sx | 可选。开始剪切的 x 坐标位置。 |
sy | 可选。开始剪切的 y 坐标位置。 |
swidth | 可选。被剪切图像的宽度。 |
sheight | 可选。被剪切图像的高度。 |
x | 在画布上放置图像的 x 坐标位置。 |
y | 在画布上放置图像的 y 坐标位置。 |
width | 可选。要使用的图像的宽度。(伸展或缩小图像) |
height | 可选。要使用的图像的高度。(伸展或缩小图像) |
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
})
复制代码