Skip to content

Commit

Permalink
feat: add screen capture
Browse files Browse the repository at this point in the history
  • Loading branch information
eepson123tw committed Oct 23, 2024
1 parent 6e1f896 commit b320733
Show file tree
Hide file tree
Showing 2 changed files with 273 additions and 0 deletions.
268 changes: 268 additions & 0 deletions fet-trick/js/screen-capture.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Screen Capture</title>
<!-- 引入 Google 字體 -->
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
<style>
/* 重置一些預設樣式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family: 'Roboto', sans-serif;
background: linear-gradient(135deg, #f5f7fa, #c3cfe2);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}

h2 {
margin-top: 20px;
color: #333;
}

p {
max-width: 600px;
text-align: center;
margin-bottom: 30px;
color: #555;
line-height: 1.6;
}

/* 按鈕樣式 */
.button {
display: inline-block;
padding: 12px 24px;
margin: 10px;
font-size: 16px;
font-weight: 700;
color: #fff;
background: #6200ea;
border: none;
border-radius: 30px;
cursor: pointer;
transition: background 0.3s, transform 0.2s;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
text-align: center;
text-decoration: none;
}

.button:hover {
background: #3700b3;
transform: translateY(-2px);
}

.button:active {
background: #290a8c;
transform: translateY(0);
}

/* 特殊樣式的下載按鈕 */
#downloadButton {
background: #03dac6;
}

#downloadButton:hover {
background: #018786;
}

/* 佈局 */
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
width: 100%;
max-width: 1200px;
gap: 40px;
}

.left, .right {
flex: 1 1 300px;
display: flex;
flex-direction: column;
align-items: center;
}

video {
border-radius: 15px;
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
margin-top: 15px;
max-width: 100%;
height: auto;
}

/* 日誌區域 */
.bottom {
width: 100%;
max-width: 800px;
margin-top: 40px;
background: #fff;
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
overflow: auto;
}

pre {
white-space: pre-wrap;
word-wrap: break-word;
color: #333;
}

/* 響應式調整 */
@media (max-width: 768px) {
.container {
flex-direction: column;
align-items: center;
}

.left, .right {
width: 100%;
max-width: 400px;
}

video {
width: 100%;
height: auto;
}
}

/* 按鈕元素的樣式調整 */
button.button {
font-family: 'Roboto', sans-serif;
}
</style>
</head>
<body>
<p>
點擊 "開始錄製" 按鈕開始錄製影片幾秒鐘。您可以通過點擊 "停止錄製" 按鈕來停止錄影。"下載影片" 按鈕將下載接收到的數據(影片將以 WebM 格式下載)。
</p>

<div class="container">
<div class="left">
<button id="startButton" class="button">
開始錄製
</button>
<h2>預覽</h2>
<video id="preview" autoplay muted></video>
</div>
<div class="right">
<button id="stopButton" class="button" disabled>
停止錄製
</button>
<h2>錄影結果</h2>
<video id="recording" controls></video>
<a id="downloadButton" class="button" href="#" download="recording.webm" style="display: none;">
下載影片
</a>
</div>
</div>

<div class="bottom">
<h2>日誌</h2>
<pre id="log">等待操作...</pre>
</div>

<!-- JavaScript 功能實現 -->
<script>
const startButton = document.getElementById('startButton');
const stopButton = document.getElementById('stopButton');
const downloadButton = document.getElementById('downloadButton');
const preview = document.getElementById('preview');
const recording = document.getElementById('recording');
const log = document.getElementById('log');

let mediaRecorder;
let recordedChunks = [];

// 檢查瀏覽器是否支持MediaRecorder
if (!('MediaRecorder' in window)) {
log.textContent = '抱歉,您的瀏覽器不支持錄影功能。';
startButton.disabled = true;
}
const displayMediaOptions = {
video: {
displaySurface: "browser",
},
audio: {
suppressLocalAudioPlayback: false,
},
preferCurrentTab: false,
selfBrowserSurface: "exclude",
systemAudio: "include",
surfaceSwitching: "include",
monitorTypeSurfaces: "include",
};

startButton.addEventListener('click', async () => {
log.textContent = '請求攝像頭和麥克風權限...';
startButton.disabled = true;
stopButton.disabled = false;
downloadButton.style.display = 'none';
recording.src = '';
try {
const stream = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
preview.srcObject = stream;
const options = { mimeType: 'video/webm; codecs=vp9' };
mediaRecorder = new MediaRecorder(stream, options);

mediaRecorder.ondataavailable = event => {
if (event.data.size > 0) {
recordedChunks.push(event.data);
log.textContent += '\n獲取錄影數據...';
}
};

mediaRecorder.onstart = () => {
log.textContent += '\n錄影開始。';
};

mediaRecorder.onstop = () => {
log.textContent += '\n錄影停止,處理錄影數據。';
const blob = new Blob(recordedChunks, { type: 'video/webm' });
const url = URL.createObjectURL(blob);
recording.src = url;
downloadButton.href = url;
downloadButton.style.display = 'inline-block';
recordedChunks = [];
log.textContent += '\n錄影完成。';
};

mediaRecorder.onerror = event => {
log.textContent += `\n錄影錯誤: ${event.error}`;
};

mediaRecorder.start();
log.textContent += '\n正在錄影中...';
} catch (err) {
log.textContent = `無法訪問攝像頭和麥克風: ${err}`;
startButton.disabled = false;
stopButton.disabled = true;
}
});

stopButton.addEventListener('click', () => {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
const stream = preview.srcObject;
if (stream) {
const tracks = stream.getTracks();
tracks.forEach(track => track.stop());
preview.srcObject = null;
}
stopButton.disabled = true;
startButton.disabled = false;
log.textContent += '\n已發出停止錄影命令。';
}
});
</script>
</body>
</html>
5 changes: 5 additions & 0 deletions src/types/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ export const jsGroups: Array<Link> = [
url: "/js/flip-animation",
group: Group.Js,
},
{
routeName: "screen-capture",
url: "/js/screen-capture",
group: Group.Js,
},
];
export const cssGroups: Array<Link> = [
{
Expand Down

0 comments on commit b320733

Please sign in to comment.