1
This commit is contained in:
257
catch-script/recorder2.js
Normal file
257
catch-script/recorder2.js
Normal file
@@ -0,0 +1,257 @@
|
||||
(function () {
|
||||
console.log("recorder2.js Start");
|
||||
if (document.getElementById("catCatchRecorder2")) {
|
||||
return;
|
||||
}
|
||||
if (!navigator.mediaDevices) {
|
||||
alert("当前网页不支持屏幕分享");
|
||||
return;
|
||||
}
|
||||
|
||||
let language = navigator.language.replace("-", "_");
|
||||
if (window.CatCatchI18n) {
|
||||
if (!window.CatCatchI18n.languages.includes(language)) {
|
||||
language = language.split("_")[0];
|
||||
if (!window.CatCatchI18n.languages.includes(language)) {
|
||||
language = "en";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加style
|
||||
const style = document.createElement("style");
|
||||
style.innerHTML = `
|
||||
@keyframes color-change{
|
||||
0% { outline: 4px solid rgb(26, 115, 232); }
|
||||
50% { outline: 4px solid red; }
|
||||
100% { outline: 4px solid rgb(26, 115, 232); }
|
||||
}
|
||||
#catCatchRecorder2 {
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
cursor: move;
|
||||
z-index: 999999999;
|
||||
outline: 4px solid rgb(26, 115, 232);
|
||||
resize: both;
|
||||
overflow: hidden;
|
||||
height: 720px;
|
||||
width: 1024px;
|
||||
top: 30%;
|
||||
left: 30%;
|
||||
pointer-events: none;
|
||||
font-size: 10px;
|
||||
}
|
||||
#catCatchRecorderHeader {
|
||||
background: rgb(26, 115, 232);
|
||||
color: white;
|
||||
text-align: center;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
pointer-events: auto;
|
||||
}
|
||||
#catCatchRecorderTitle {
|
||||
cursor: move;
|
||||
user-select: none;
|
||||
width: 45%;
|
||||
}
|
||||
#catCatchRecorderinnerCropArea {
|
||||
height: calc(100% - 20px);
|
||||
width: 100%;
|
||||
}
|
||||
.animation {
|
||||
animation: color-change 5s infinite;
|
||||
}
|
||||
.input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.input-group label {
|
||||
margin-right: 5px;
|
||||
}
|
||||
#videoBitrate, #audioBitrate {
|
||||
width: 4rem;
|
||||
}
|
||||
.input-group label{
|
||||
width: 5rem;
|
||||
}`;
|
||||
|
||||
// 添加div
|
||||
let cat = document.createElement("div");
|
||||
cat.setAttribute("id", "catCatchRecorder2");
|
||||
cat.innerHTML = `<div id="catCatchRecorderinnerCropArea"></div>
|
||||
<div id="catCatchRecorderHeader">
|
||||
<div class="input-group">
|
||||
<select id="videoBits">
|
||||
<option value="2500000" data-i18n="videoBits">视频码率</option>
|
||||
<option value="2500000">2.5 Mbps</option>
|
||||
<option value="5000000">5 Mbps</option>
|
||||
<option value="8000000">8 Mbps</option>
|
||||
<option value="16000000">16 Mbps</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<select id="audioBits">
|
||||
<option value="128000" data-i18n="audioBits">视频码率</option>
|
||||
<option value="128000">128 kbps</option>
|
||||
<option value="256000">256 kbps</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="catCatchRecorderStart" data-i18n="startRecording">开始录制</div>
|
||||
<div id="catCatchRecorderTitle" data-i18n="dragWindow">拖动窗口</div>
|
||||
<div id="catCatchRecorderClose" data-i18n="close">关闭</div>
|
||||
</div>`;
|
||||
|
||||
// 创建 Shadow DOM 放入CatCatch
|
||||
const divShadow = document.createElement('div');
|
||||
const shadowRoot = divShadow.attachShadow({ mode: 'closed' });
|
||||
shadowRoot.appendChild(cat);
|
||||
shadowRoot.appendChild(style);
|
||||
document.getElementsByTagName('html')[0].appendChild(divShadow);
|
||||
|
||||
// 事件绑定
|
||||
const catCatchRecorderStart = cat.querySelector("#catCatchRecorderStart");
|
||||
catCatchRecorderStart.onclick = function () {
|
||||
if (recorder) {
|
||||
recorder.stop();
|
||||
return;
|
||||
}
|
||||
try { startRecording(); } catch (e) { console.log(e); return; }
|
||||
}
|
||||
cat.querySelector("#catCatchRecorderClose").onclick = function () {
|
||||
recorder && recorder.stop();
|
||||
cat.remove();
|
||||
}
|
||||
|
||||
// 拖动div
|
||||
const catCatchRecorderinnerCropArea = cat.querySelector("#catCatchRecorderinnerCropArea");
|
||||
cat.querySelector("#catCatchRecorderTitle").onpointerdown = (e) => {
|
||||
let pos1, pos2, pos3, pos4;
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
if (pos3 - cat.offsetWidth - cat.offsetLeft > - 20 &&
|
||||
pos4 - cat.offsetHeight - cat.offsetTop > - 20) {
|
||||
return;
|
||||
}
|
||||
document.onpointermove = (e) => {
|
||||
pos1 = pos3 - e.clientX;
|
||||
pos2 = pos4 - e.clientY;
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
cat.style.top = cat.offsetTop - pos2 + "px";
|
||||
cat.style.left = cat.offsetLeft - pos1 + "px";
|
||||
}
|
||||
document.onpointerup = () => {
|
||||
document.onpointerup = null;
|
||||
document.onpointermove = null;
|
||||
}
|
||||
}
|
||||
// document.getElementsByTagName('html')[0].appendChild(cat);
|
||||
|
||||
// 初始化位置
|
||||
const video = document.querySelector("video");
|
||||
if (video) {
|
||||
// 调整和video一样大小
|
||||
if (video.clientHeight >= 0 && video.clientWidth >= 0) {
|
||||
cat.style.height = video.clientHeight + 20 + "px";
|
||||
cat.style.width = video.clientWidth + "px";
|
||||
}
|
||||
// 调整到video的位置
|
||||
const videoOffset = getElementOffset(video);
|
||||
if (videoOffset.top >= 0 && videoOffset.left >= 0) {
|
||||
cat.style.top = videoOffset.top + "px";
|
||||
cat.style.left = videoOffset.left + "px";
|
||||
}
|
||||
// 防止遮挡菜单
|
||||
let catAttr = cat.getBoundingClientRect();
|
||||
if (document.documentElement.scrollTop + catAttr.bottom > document.documentElement.scrollTop + window.innerHeight) {
|
||||
cat.style.top = document.documentElement.scrollTop + window.innerHeight - catAttr.height + "px";
|
||||
}
|
||||
}
|
||||
|
||||
// 录制
|
||||
var recorder;
|
||||
async function startRecording() {
|
||||
const buffer = [];
|
||||
let option = {
|
||||
mimeType: 'video/webm;codecs=vp8,opus',
|
||||
videoBitsPerSecond: +cat.querySelector("#videoBits").value,
|
||||
audioBitsPerSecond: +cat.querySelector("#audioBits").value
|
||||
};
|
||||
|
||||
if (MediaRecorder.isTypeSupported('video/webm;codecs=vp9,opus')) {
|
||||
option.mimeType = 'video/webm;codecs=vp9,opus';
|
||||
} else if (MediaRecorder.isTypeSupported('video/webm;codecs=h264')) {
|
||||
option.mimeType = 'video/webm;codecs=h264';
|
||||
}
|
||||
const cropTarget = await CropTarget.fromElement(catCatchRecorderinnerCropArea);
|
||||
const stream = await navigator.mediaDevices
|
||||
.getDisplayMedia({
|
||||
preferCurrentTab: true,
|
||||
video: {
|
||||
cursor: "never"
|
||||
},
|
||||
audio: {
|
||||
sampleRate: 48000,
|
||||
sampleSize: 16,
|
||||
channelCount: 2
|
||||
}
|
||||
});
|
||||
const [track] = stream.getVideoTracks();
|
||||
await track.cropTo(cropTarget);
|
||||
recorder = new MediaRecorder(stream, option);
|
||||
recorder.start();
|
||||
recorder.onstart = function (e) {
|
||||
buffer.slice(0);
|
||||
catCatchRecorderStart.innerHTML = i18n("stopRecording", "停止录制");
|
||||
cat.classList.add("animation");
|
||||
}
|
||||
recorder.ondataavailable = function (e) {
|
||||
buffer.push(e.data);
|
||||
}
|
||||
recorder.onstop = function () {
|
||||
const fileBlob = new Blob(buffer, { type: option });
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(fileBlob);
|
||||
a.download = `${document.title}.webm`;
|
||||
a.click();
|
||||
a.remove();
|
||||
buffer.slice(0);
|
||||
stream.getTracks().forEach(track => track.stop());
|
||||
recorder = undefined;
|
||||
catCatchRecorderStart.innerHTML = i18n("startRecording", "开始录制");
|
||||
cat.classList.remove("animation");
|
||||
}
|
||||
}
|
||||
function getElementOffset(el) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
||||
return {
|
||||
top: rect.top + scrollTop,
|
||||
left: rect.left + scrollLeft
|
||||
};
|
||||
}
|
||||
|
||||
// i18n
|
||||
if (window.CatCatchI18n) {
|
||||
CatCatch.querySelectorAll('[data-i18n]').forEach(function (element) {
|
||||
const translation = window.CatCatchI18n[element.dataset.i18n]?.[language];
|
||||
if (translation) {
|
||||
element.innerHTML = translation;
|
||||
}
|
||||
});
|
||||
CatCatch.querySelectorAll('[data-i18n-outer]').forEach(function (element) {
|
||||
const outerTranslation = window.CatCatchI18n[element.dataset.i18nOuter]?.[language];
|
||||
if (outerTranslation) {
|
||||
element.outerHTML = outerTranslation;
|
||||
}
|
||||
});
|
||||
}
|
||||
function i18n(key, original = "") {
|
||||
if (!window.CatCatchI18n) { return original };
|
||||
return window.CatCatchI18n[key][language];
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user