录像原理
- 创建一个画布,video标签本身不具备记录画面功能,所以我们需要通过Canvas来达成这个功能
- 创建一个录制器,与CanvasStream绑定,这样画布绘制什么,录制器都能触发回调
- 创建一个定时器,不停通过CanvasContext来捕捉video标签画面,然后绘制到Canvas上面
- 创建一个字节数组,只要Canvas捕捉到新画面,就会触发MediaRecorder数据回调,将拿到的帧数据写入数组
- 每个帧数据本身就是一个Blob[]数据结构,将所有的Blob数组合并,得到一个最终的Blob,就是录制的视频数据
截图原理
- 同录像一样,创建一个Canvas来捕捉video标签画面
- 通过Canvas2Image库将Canvas当前画面数据以base64格式写到一个img标签里面
下载原理
- 录制出来的Blob字节块,可以通过window.URL.createObjectURL转化为一个BlobURL,将BlobURL作为a标签的链接地址,即可在点击时下载该字节块数据
- base64格式的图像数据,可以直接作为a标签的链接地址,点击时,浏览器会自动将其解码为图片文件对应的字节块,供用户下载
- 通过a标签的download属性,可以设置下载的文件名
注意事项
- 截图功能不但可以截取video标签,还可以对其它任意元素/整个网页进行抓取,但必须先通过Html2Canvas库将对应的元素绘制到canvas上面,此处不多说,有兴趣自己尝试
- 这些功能均只支持相同域名下的文件/媒体流录制,不支持跨域录制,即无法录制来自其它网站的图片视频等资源
MP4文件录像代码
File Record
开始录制
结束录制
* {
margin: 20px;
font-size: 0px;
box-sizing: border-box;
}
#canvas {
display: none;
}
#video {
width: 640px;
height: 360px;
background: deepskyblue;
display: block;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
}
#button-start {
width: 300px;
height: 75px;
box-sizing: border-box;
background: linear-gradient(#FF000011, #FF000044) orange;
color: white;
font-size: 16px;
font-family: "monospace";
border: none;
outline: none;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
user-select: none;
}
#button-start:hover {
background: linear-gradient(#FF000033, #FF000077) orange;
}
#button-start:active {
background: linear-gradient(#FF000055, #FF000099) orange;
}
#button-start[disabled] {
background: gray;
}
#button-stop {
width: 300px;
height: 75px;
box-sizing: border-box;
background: linear-gradient(#9933FF00, #9933FF33) dodgerblue;
color: white;
font-size: 16px;
font-family: "monospace";
border: none;
outline: none;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
user-select: none;
}
#button-stop:hover {
background: linear-gradient(#9933FF22, #9933FF66) dodgerblue;
}
#button-stop:active {
background: linear-gradient(#9933FF44, #9933FF88) dodgerblue;
}
#button-stop[disabled] {
background: gray;
}
let canvasElement = document.querySelector("#canvas");
let videoElement = document.querySelector("#video");
let startButton = document.querySelector("#button-start");
let stopButton = document.querySelector("#button-stop");
const videoWidth = 640;
const videoHeight = 360;
const frameRate = 60;
const encodeType = "video/webm;codecs=vp8";
let chunks = [];
let frameId = null;
//设置画布背景
const canvasContext = canvasElement.getContext("2d");
canvasContext.fillStyle = "deepskyblue";
canvasContext.fillRect(0, 0, canvasElement.width, canvasElement.height);
//创建MediaRecorder,设置媒体参数
const stream = canvasElement.captureStream(frameRate);
const recorder = new MediaRecorder(stream, {
mimeType: encodeType
});
//收集录制数据
recorder.ondataavailable = e => {
chunks.push(e.data);
};
//按钮事件
startButton.disabled = false;
stopButton.disabled = true;
startButton.onclick = e => {
startButton.disabled = true;
stopButton.disabled = false;
recorder.start(10);
drawFrame();
};
stopButton.onclick = e => {
startButton.disabled = false;
stopButton.disabled = true;
recorder.stop();
cancelAnimationFrame(frameId);
download();
};
//播放视频
function drawFrame() {
canvasContext.drawImage(videoElement, 0, 0, videoWidth, videoHeight);
frameId = requestAnimationFrame(drawFrame);
}
//下载录制内容
function download() {
let blob = new Blob(chunks);
let url = window.URL.createObjectURL(blob);
let link = document.createElement("a");
link.href = url;
link.download = new Date().getTime() + ".mp4";
link.style.display = "none";
document.body.appendChild(link);
link.click();
link.remove();
}
摄像头录像代码
Camera Record
开始录制
结束录制
* {
margin: 20px;
font-size: 0px;
box-sizing: border-box;
}
#canvas {
display: none;
}
#video {
width: 640px;
height: 360px;
background: deepskyblue;
display: block;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
}
#button-start {
width: 300px;
height: 75px;
box-sizing: border-box;
background: linear-gradient(#FF000011, #FF000044) orange;
color: white;
font-size: 16px;
font-family: "monospace";
border: none;
outline: none;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
user-select: none;
}
#button-start:hover {
background: linear-gradient(#FF000033, #FF000077) orange;
}
#button-start:active {
background: linear-gradient(#FF000055, #FF000099) orange;
}
#button-start[disabled] {
background: gray;
}
#button-stop {
width: 300px;
height: 75px;
box-sizing: border-box;
background: linear-gradient(#9933FF00, #9933FF33) dodgerblue;
color: white;
font-size: 16px;
font-family: "monospace";
border: none;
outline: none;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
user-select: none;
}
#button-stop:hover {
background: linear-gradient(#9933FF22, #9933FF66) dodgerblue;
}
#button-stop:active {
background: linear-gradient(#9933FF44, #9933FF88) dodgerblue;
}
#button-stop[disabled] {
background: gray;
}
let canvasElement = document.querySelector("#canvas");
let videoElement = document.querySelector("#video");
let startButton = document.querySelector("#button-start");
let stopButton = document.querySelector("#button-stop");
const videoWidth = 640;
const videoHeight = 360;
const frameRate = 60;
const encodeType = "video/webm;codecs=vp8";
let chunks = [];
let frameId = null;
//设置画布背景
const canvasContext = canvasElement.getContext("2d");
canvasContext.fillStyle = "deepskyblue";
canvasContext.fillRect(0, 0, canvasElement.width, canvasElement.height);
//创建MediaRecorder,设置媒体参数
const stream = canvasElement.captureStream(frameRate);
const recorder = new MediaRecorder(stream, {
mimeType: encodeType
});
//收集录制数据
recorder.ondataavailable = e => {
chunks.push(e.data);
};
//按钮事件
startButton.disabled = true;
stopButton.disabled = true;
startButton.onclick = e => {
startButton.disabled = true;
stopButton.disabled = false;
recorder.start(10);
drawFrame();
};
stopButton.onclick = e => {
startButton.disabled = false;
stopButton.disabled = true;
recorder.stop();
cancelAnimationFrame(frameId);
download();
};
//打开摄像头,并将数据显示到video标签上
navigator.mediaDevices.getUserMedia({
audio: false,
video: true
}).then(mediaStream => {
videoElement.srcObject = mediaStream;
videoElement.play();
startButton.disabled = false;
}).catch(error => {
alert("打开摄像头失败");
});
//播放视频
function drawFrame() {
canvasContext.drawImage(videoElement, 0, 0, videoWidth, videoHeight);
frameId = requestAnimationFrame(drawFrame);
}
//下载录制内容
function download() {
let blob = new Blob(chunks);
let url = window.URL.createObjectURL(blob);
let link = document.createElement("a");
link.href = url;
link.download = new Date().getTime() + ".mp4";
link.style.display = "none";
document.body.appendChild(link);
link.click();
link.remove();
}
MP4文件截图代码
Canvas to Image
屏幕截图
* {
margin: 5px;
font-size: 0px;
box-sizing: border-box;
}
#video {
width: 640px;
height: 360px;
background: deepskyblue;
display: block;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
}
#button-start {
width: 640px;
height: 75px;
box-sizing: border-box;
background: linear-gradient(#FF000011, #FF000044) orange;
color: white;
font-size: 16px;
font-family: "monospace";
border: none;
outline: none;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
user-select: none;
}
#button-start:hover {
background: linear-gradient(#FF000033, #FF000077) orange;
}
#button-start:active {
background: linear-gradient(#FF000055, #FF000099) orange;
}
let canvasElement = document.querySelector("#canvas");
let videoElement = document.querySelector("#video");
let startButton = document.querySelector("#button-start");
const videoWidth = 640;
const videoHeight = 360;
let frameId = null;
//设置画布背景
const canvasContext = canvasElement.getContext("2d");
canvasContext.fillStyle = "deepskyblue";
canvasContext.fillRect(0, 0, canvasElement.width, canvasElement.height);
//按钮事件
startButton.onclick = e => {
download();
};
//将video标签绘制到canvas上面
drawFrame();
//播放视频
function drawFrame() {
canvasContext.drawImage(videoElement, 0, 0, videoWidth, videoHeight);
frameId = requestAnimationFrame(drawFrame);
}
//下载录制内容
function download() {
let link = document.createElement("a");
link.href = Canvas2Image.saveAsPNG(canvasElement, true).src;
link.download = new Date().getTime() + ".png";
link.style.display = "none";
document.body.appendChild(link);
link.click();
link.remove();
}
源码下载
下载地址:video_record.7z