导言
一切的一切都开端于那个夜黑风高的夜晚……
那晚,我急需求一款录屏软件,找来找去,可是:
付费去广告、付费去水印、付费高清、付费下载😅……
等等,忽然我灵光乍现,我还有一条路:
自己做!
所以原生Js基于WebRTC完成的Web端实时录屏功用就出来了!文末附开源地址😎
先来试用一下成品吧→SHTRec←移动端暂不支撑哦
技术选型
- WebRTC
- 原生Js
中心代码完成
html
整个的录屏功用区域都放在一个类名为main的div里边
类名为btn的div放上开端录屏button,并给button绑定start()函数,类名为rec
<div class="main">
<div class="btn">
<button onclick="start()" class="rec">点击录屏</button>
</div>
</div>
Js
js这儿大体分为四部分:开端、完毕、播映、下载
function start() {
......
/* 开端 */
}
function stop() {
......
/* 完毕 */
}
function replay() {
......
/* 播映 */
}
function download() {
......
/* 下载 */
}
为了进步代码可读性,先声明一些需求用到的全局变量
let stream;
let mediaRecorder;
let blobs;
let blob;
let url;
let videoTracks;
let ale;
let span;
let word;
let delLastSpan;
let delSpan;
let video;
let a;
start()
咱们运用Web API中的navigator.mediaDevices.getDisplayMedia
办法来获取屏幕共享(包括音频和视频)的流
function start() {
navigator.mediaDevices.getDisplayMedia({ audio:true,video: true })
}
navigator.mediaDevices是Web API的一部分,它供给了拜访用户的媒体输入设备(如摄像头、麦克风)和输出设备(如显示器、扬声器)的接口。
但咱们只需求获取表明屏幕或应用程序窗口的媒体流,所以用到了getDisplayMedia
{ audio:true, video: true } 是一个约束目标,它指定了咱们想要的媒体流的类型。咱们需求获取的是包含音频(audio: true
)和视频(video: true
)的媒体流。
这个办法会回来一个Promise,该Promise在成功时解析会为一个MediaStream
目标,该目标表明屏幕和音频的媒体流。随后咱们将这个流传递给<video>
元素
Promise成功后,会履行一个then异步操作办法
.then(function (s) {
stream = s;
mediaRecorder = new MediaRecorder(stream, {
mimeType: 'video/webm'
});
blobs = [], mediaRecorder;
videoTracks = stream.getVideoTracks();
if (videoTracks.length > 0) {
mediaRecorder.start(100);
mediaRecorder.ondataavailable = (e) => {
blobs.push(e.data);
};
document.body.innerHTML = `
<div class="main">
<div class="container">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div> `;
}
})
s
是Promise的成果。
然后咱们将Promise的成果赋值给 stream
变量: stream = s;
mediaRecorder = new MediaRecorder(stream, {mimeType: 'video/webm'});
:创立一个新的 MediaRecorder 目标,它将用于记载 stream
中的媒体内容。在这儿咱们设置了mimeType为 ‘video/webm’,因此记载的媒体内容将是 webm 格局的视频。
咱们是不是会以为blobs = [], mediaRecorder;
这行代码有些问题?它试图将两个不同的目标赋值给 blobs
,这在 JavaScript 中是不合法的。你以为应该这样写:blobs = [mediaRecorder];
?然而并不是,这行代码的意思是创立一个新的数组,其中包含两个元素:一个空数组(用来寄存mediaRecorder
的可用数据)和 mediaRecorder
目标,然后将这个新数组赋值给 blobs
变量。
videoTracks = stream.getVideoTracks();
是从 stream
中获取一切的视频轨迹,并将它们存储在 videoTracks
变量中。
if (videoTracks.length > 0) {...}
这行代码是在检查 videoTracks
中是否有视频轨迹。假如有,咱们才干将咱们的项目继续下去
mediaRecorder.start(100);
这行代码将开端 mediaRecorder
的录制,参数100意味着每100毫秒,mediaRecorder
会生成一个新的数据片段。
mediaRecorder.ondataavailable = (e) => {blobs.push(e.data);};
这行代码是在当 mediaRecorder
开端录制后产生可用数据时,将数据增加到 blobs
数组中。
document.body.innerHTML = "...";
这行代码是在将HTML代码赋值给 document.body.innerHTML
,以动态改动html内容的办法来营造一个完美的用户体验,增加的HTML代码可所以一个动画来表明正在录屏,如下所示:
这个动画对应的css样式为
:root {
--h: 280px;
}
.container {
display: flex;
margin: 0 auto;
align-items: center;
height: var(--h);
}
.container span {
background: linear-gradient(to top, #d299c2 0%, #fef9d7 100%);
width: 50px;
height: 20%;
border-radius: calc(var(--h) * 0.2 * 0.5);
margin-right: 4px;
animation: loading 2.5s infinite linear;
animation-delay: calc(0.2s * var(--d));
}
.container span:last-child {
margin-right: 0px;
}
@keyframes loading {
0% {
background-image: linear-gradient(to right, #fa709a 0%, #fee140 100%);
height: 20%;
border-radius: calc(var(--h) * 0.2 * 0.5);
}
50% {
background-image: linear-gradient(to top, #d299c2 0%, #fef9d7 100%);
height: 100%;
border-radius: calc(var(--h) * 1 * 0.5);
}
100% {
background-image: linear-gradient(to top, #a8edea 0%, #fed6e3 100%);
height: 20%;
border-radius: calc(var(--h) * 0.2 * 0.5);
}
}
同时呢用户可能会拒绝给你权限来录制屏幕或产生一些其他的过错,那么就需求针对Promise的过错情况来做一些处理
.catch(function (error) {
console.log(error);
});
.catch(function (error) {...})
是Promise的catch办法,它将在Promise产生过错时履行。error
是产生的过错目标。
然后咱们用console.log(error);
在操控台中打印出过错信息,让咱们能够在操控台中看到过错的详细信息,然后更容易地调试和解决问题。
stop()
开端录制当然也要中止录制了,通过创立一个stop函数来操控录制
function stop() {
if (stream) {
stream.getTracks().forEach(track => track.stop());
}
document.body.innerHTML = `
<div class="main">
<div class="scr">
<span class="stopLog">录制完毕!</span>
</div>
</div> `;
}
咱们首要检查是否存在一个名为stream
的变量。假如stream
存在(也便是说,它不是null
或许undefined
),那么代码就会履行花括号里边的语句来中止录制。
假如stream不是null
或许undefined``那么stream.getTracks()
办法就会被调用。这个办法回来一个包含流中一切轨迹的数组。
然后,用forEach()
办法被用于遍历这个数组。关于数组中的每一个轨迹(track
),都会调用它的stop()
办法,以中止该轨迹。
document.body.innerHTML = "...";
这行代码是在将HTML代码赋值给 document.body.innerHTML
,以动态改动html内容的办法来提示用户录制完毕,如下图所示:
replay()
当录制完毕后咱们需求将录制好的视频供用户播映。
function replay() {
blob = new Blob(blobs, {
type: 'video/webm'
});
document.body.innerHTML = `
<div class="main">
<div class="scr">
<video class="vid" autoplay controls width="800px" height="280px"></video>
</div>
</div> `;
video = document.querySelector(".vid");
video.src = URL.createObjectURL(blob);
}
首要创立一个新的Blob
目标,并运用从前存储的blobs
数组和媒体类型'video/webm'
。
Blob
目标表明一段二进制数据,在这儿,它表明的是视频文件。
document.body.innerHTML = "...";
这行代码和之前的是一个道理,将HTML代码赋值给 document.body.innerHTML
,以动态改动html内容的办法来显示录制好的视频
接下来,获取这个新的video
元素,并将其src
特点设置为新创立的Blob
目标的URL。这是通过调用URL.createObjectURL(blob)
完成的,这个办法会回来一个新的URL,代表blob
目标。这样,video
元素就能够加载并播映这个视频了。
video
元素具有autoplay
和controls
特点,意味着视频将在加载后主动播映,并且用户能够操控视频的播映。
download()
运用录屏功用的底子目的还是为了下载到本地,所以咱们再创立一个下载函数
function download() {
blob = new Blob(blobs, {
type: 'video/webm'
});
url = URL.createObjectURL(blob);
a = document.createElement('a');
a.href = url;
a.download = 'record.webm';
a.style.display = 'none';
a.click();
}
这儿和replay()函数的原理大体一致,通过调用URL.createObjectURL(blob)
回来一个新的URL,代表blob
目标。
接下来创立一个新的<a>
元素,并将其href
特点设置为新创立的URL
然后将<a>
元素的download
特点设置为'record.webm'
,也便是将视频文件将被命名为record.webm
。
接着将<a>
元素的style.display
特点设置为'none'
,这样用户在页面上就看不到这个链接了
最终调用<a>
元素的click()
办法,这会模拟用户点击链接的行为,然后主动下载视频文件。
结语
看到到这儿,完成屏幕的录制、回放、下载这个功用完成起来是不是也没有很难了?
下面是GitHub开源地址,假如这篇文章对你有所启示,欢迎点赞收藏+转发😁