<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>相机</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
background-color: #f0f0f0;
padding: 20px;
}
h1 {
color: #333;
margin-bottom: 20px;
}
.camera-container {
position: relative;
width: 100%;
max-width: 640px;
margin-bottom: 20px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
#video {
width: 100%;
background-color: #000;
display: block;
}
#canvas {
display: none;
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
justify-content: center;
}
button {
padding: 10px 20px;
font-size: 16px;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
#startButton {
background-color: #4285f4;
}
#captureButton {
background-color: #0f9d58;
}
#startRecordButton {
background-color: #db4437;
}
#stopRecordButton {
background-color: #ff6d00;
}
#stopButton {
background-color: #9e9e9e;
}
button:hover {
opacity: 0.9;
}
button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.gallery {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
width: 100%;
max-width: 800px;
}
.media-item {
position: relative;
width: 150px;
height: 150px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.photo {
width: 100%;
height: 100%;
object-fit: cover;
}
.video-thumbnail {
width: 100%;
height: 100%;
object-fit: cover;
}
.play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 40px;
height: 40px;
background-color: rgba(255, 255, 255, 0.7);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #db4437;
font-size: 20px;
}
.permission-error {
color: #d32f2f;
text-align: center;
padding: 20px;
display: none;
}
.timer {
position: absolute;
top: 10px;
right: 10px;
background-color: rgba(0, 0, 0, 0.5);
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 14px;
display: none;
}
</style>
</head>
<body>
<h1>摄像机</h1>
<div class="camera-container">
<video id="video" autoplay="" playsinline=""></video>
<canvas id="canvas"></canvas>
<div id="timer" class="timer">00:00</div>
<div id="permissionError" class="permission-error">
<h2>无法访问摄像头</h2>
<p>请确保已授予摄像头访问权限,并且您的设备有可用的摄像头。</p>
</div>
</div>
<div class="controls">
<button id="startButton">打开摄像头</button>
<button id="captureButton" disabled="">拍照</button>
<button id="startRecordButton" disabled="">开始录像</button>
<button id="stopRecordButton" disabled="">停止录像</button>
<button id="stopButton" disabled="">关闭摄像头</button>
</div>
<div class="gallery" id="mediaGallery"></div>
<script>
// 获取DOM元素
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const startButton = document.getElementById('startButton');
const captureButton = document.getElementById('captureButton');
const startRecordButton = document.getElementById('startRecordButton');
const stopRecordButton = document.getElementById('stopRecordButton');
const stopButton = document.getElementById('stopButton');
const mediaGallery = document.getElementById('mediaGallery');
const permissionError = document.getElementById('permissionError');
const timer = document.getElementById('timer');
let stream = null;
let mediaRecorder = null;
let recordedChunks = [];
let recordingStartTime = 0;
let timerInterval = null;
// 打开摄像头
startButton.addEventListener('click', async () => {
try {
// 请求摄像头访问权限
stream = await navigator.mediaDevices.getUserMedia({
video: {
width: { ideal: 1280 },
height: { ideal: 720 },
facingMode: 'environment' // 优先使用后置摄像头
},
audio: true // 启用音频录制
});
// 将视频流显示在video元素上
video.srcObject = stream;
// 启用控制按钮
captureButton.disabled = false;
startRecordButton.disabled = false;
stopButton.disabled = false;
startButton.disabled = true;
// 隐藏错误信息
permissionError.style.display = 'none';
} catch (err) {
console.error('摄像头访问错误:', err);
permissionError.style.display = 'block';
}
});
// 拍照
captureButton.addEventListener('click', () => {
// 设置canvas尺寸与视频相同
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
// 在canvas上绘制当前视频帧
const context = canvas.getContext('2d');
context.drawImage(video, 0, 0, canvas.width, canvas.height);
// 将canvas内容转换为图像URL
const imageUrl = canvas.toDataURL('image/png');
// 创建新的图像元素并添加到媒体库
createMediaItem(imageUrl, 'photo');
// 保存到本地存储
saveMediaToLocalStorage(imageUrl, 'photo');
});
// 开始录像
startRecordButton.addEventListener('click', () => {
recordedChunks = [];
// 创建媒体录制器
mediaRecorder = new MediaRecorder(stream, {
mimeType: 'video/webm'
});
// 收集数据块
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
recordedChunks.push(event.data);
}
};
// 录像完成时处理
mediaRecorder.onstop = () => {
// 创建Blob对象
const blob = new Blob(recordedChunks, { type: 'video/webm' });
const videoUrl = URL.createObjectURL(blob);
// 创建新的视频元素并添加到媒体库
createMediaItem(videoUrl, 'video');
// 保存到本地存储
saveMediaToLocalStorage(videoUrl, 'video');
// 重置按钮状态
startRecordButton.disabled = false;
stopRecordButton.disabled = true;
// 停止计时器
clearInterval(timerInterval);
timer.style.display = 'none';
};
// 开始录制
mediaRecorder.start(100); // 每100ms收集一次数据
// 记录开始时间并启动计时器
recordingStartTime = Date.now();
updateTimer();
timerInterval = setInterval(updateTimer, 1000);
timer.style.display = 'block';
// 更新按钮状态
startRecordButton.disabled = true;
stopRecordButton.disabled = false;
});
// 更新计时器显示
function updateTimer() {
const elapsed = Math.floor((Date.now() - recordingStartTime) / 1000);
const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0');
const seconds = (elapsed % 60).toString().padStart(2, '0');
timer.textContent = `${minutes}:${seconds}`;
}
// 停止录像
stopRecordButton.addEventListener('click', () => {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
}
});
// 关闭摄像头
stopButton.addEventListener('click', () => {
// 停止录像(如果正在录制)
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
}
if (stream) {
// 停止所有视频轨道
stream.getTracks().forEach(track => track.stop());
video.srcObject = null;
stream = null;
// 重置按钮状态
captureButton.disabled = true;
startRecordButton.disabled = true;
stopRecordButton.disabled = true;
stopButton.disabled = true;
startButton.disabled = false;
// 停止计时器
clearInterval(timerInterval);
timer.style.display = 'none';
}
});
// 创建媒体项并添加到画廊
function createMediaItem(url, type) {
const mediaItem = document.createElement('div');
mediaItem.className = 'media-item';
if (type === 'photo') {
const img = document.createElement('img');
img.src = url;
img.className = 'photo';
mediaItem.appendChild(img);
} else if (type === 'video') {
const video = document.createElement('video');
video.src = url;
video.className = 'video-thumbnail';
video.setAttribute('controls', 'true');
video.style.display = 'none';
const thumbnail = document.createElement('img');
thumbnail.src = url;
thumbnail.className = 'video-thumbnail';
const playIcon = document.createElement('div');
playIcon.className = 'play-icon';
playIcon.innerHTML = '▶';
mediaItem.appendChild(thumbnail);
mediaItem.appendChild(playIcon);
mediaItem.appendChild(video);
// 点击缩略图显示视频播放器
mediaItem.addEventListener('click', () => {
thumbnail.style.display = 'none';
playIcon.style.display = 'none';
video.style.display = 'block';
video.play();
});
// 视频结束时恢复缩略图
video.addEventListener('ended', () => {
thumbnail.style.display = 'block';
playIcon.style.display = 'flex';
video.style.display = 'none';
});
}
mediaGallery.appendChild(mediaItem);
}
// 保存媒体到本地存储
function saveMediaToLocalStorage(url, type) {
let mediaItems = JSON.parse(localStorage.getItem('cameraMedia') || '[]');
mediaItems.push({ url, type, timestamp: Date.now() });
localStorage.setItem('cameraMedia', JSON.stringify(mediaItems));
}
// 页面加载时加载保存的媒体
window.addEventListener('DOMContentLoaded', () => {
const savedMedia = JSON.parse(localStorage.getItem('cameraMedia') || '[]');
savedMedia.forEach(media => {
createMediaItem(media.url, media.type);
});
});
</script>
</body></html>
© 版权声明
1、本网站名称:
X黑手网
2、本站永久网址:https://www.xheishou.com
3、本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
4、本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
5、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
6、本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
2、本站永久网址:https://www.xheishou.com
3、本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
4、本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
5、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
6、本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
THE END
暂无评论内容