记一个拍照的web页面demo
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>摄像头拍照示例</title>
<style>
:root {
--primary-color: #4CAF50;
--secondary-color: #2196F3;
--shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
body {
font-family: system-ui, -apple-system, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
h2 {
color: #333;
margin: 1.5rem 0;
}
.container {
position: relative;
width: min(90vw, 1280px);
aspect-ratio: 16/9;
margin: 1rem 0;
background: #000;
border-radius: 8px;
overflow: hidden;
box-shadow: var(--shadow);
}
#video,
#canvas {
width: 100%;
height: 100%;
object-fit: cover;
image-rendering: crisp-edges;
transform: scaleX(-1); /* 镜像翻转 */
}
.button-group {
display: flex;
gap: 1rem;
margin: 1.5rem 0;
}
button {
padding: 0.8rem 1.6rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
transition:
transform 0.2s ease,
opacity 0.2s ease;
box-shadow: var(--shadow);
}
button:active {
transform: scale(0.96);
}
#capture { background: var(--primary-color); color: white; }
#download { background: var(--secondary-color); color: white; }
#status {
color: #666;
font-size: 0.9em;
margin-top: 0.5rem;
}
#errorMsg {
color: #d32f2f;
padding: 0.8rem;
background: #ffebee;
border-radius: 4px;
margin: 1rem 0;
display: none;
max-width: 80%;
text-align: center;
}
@media (max-width: 480px) {
.button-group {
flex-direction: column;
width: 100%;
}
button {
width: 100%;
}
}
</style>
</head>
<body>
<h2>摄像头拍照演示</h2>
<div class="container">
<video id="video" autoplay playsinline></video>
<canvas id="canvas" style="display: none;"></canvas>
</div>
<div class="button-group">
<button id="capture">拍摄照片</button>
<button id="download" disabled>下载照片</button>
</div>
<div id="status">正在初始化摄像头...</div>
<div id="errorMsg"></div>
<script>
(() => {
// 配置常量
const CONFIG = {
resolutions: [
{ width: 1920, height: 1080 },
{ width: 1280, height: 720 },
{ width: 640, height: 480 }
],
jpegQuality: 0.92,
facingMode: 'environment'
};
// DOM 元素
const el = {
video: document.getElementById('video'),
canvas: document.getElementById('canvas'),
capture: document.getElementById('capture'),
download: document.getElementById('download'),
status: document.getElementById('status'),
errorMsg: document.getElementById('errorMsg')
};
let mediaStream = null;
// 摄像头初始化
const initCamera = async () => {
try {
const stream = await getOptimalStream();
handleStreamSuccess(stream);
} catch (error) {
handleStreamError(error);
}
};
const getOptimalStream = async () => {
for (const res of CONFIG.resolutions) {
try {
return await navigator.mediaDevices.getUserMedia({
video: {
facingMode: CONFIG.facingMode,
width: { ideal: res.width, max: res.width },
height: { ideal: res.height, max: res.height }
}
});
} catch (error) {
if (res === CONFIG.resolutions.at(-1)) throw error;
}
}
};
const handleStreamSuccess = (stream) => {
mediaStream = stream;
el.video.srcObject = stream;
el.video.onloadedmetadata = () => {
el.video.play();
updateResolutionStatus();
el.capture.disabled = false;
};
};
const handleStreamError = (error) => {
console.error('摄像头错误:', error);
showError(`摄像头访问失败: ${errorToString(error)}`);
el.status.style.display = 'none';
el.capture.disabled = true;
};
// 工具函数
const errorToString = (error) => {
const errors = {
NotAllowedError: '用户拒绝了权限请求',
NotFoundError: '未找到视频设备',
NotReadableError: '设备正被其他程序使用',
OverconstrainedError: '无法满足分辨率要求'
};
return errors[error.name] || error.message;
};
const updateResolutionStatus = () => {
el.status.textContent = `当前分辨率: ${el.video.videoWidth}x${el.video.videoHeight}`;
};
const showError = (message) => {
el.errorMsg.textContent = message;
el.errorMsg.style.display = 'block';
setTimeout(() => el.errorMsg.style.display = 'none', 5000);
};
// 事件处理
el.capture.addEventListener('click', () => {
const ctx = el.canvas.getContext('2d');
[el.canvas.width, el.canvas.height] = [el.video.videoWidth, el.video.videoHeight];
ctx.save();
ctx.scale(-1, 1); // 镜像处理
ctx.drawImage(el.video, -el.canvas.width, 0, el.canvas.width, el.canvas.height);
ctx.restore();
el.video.style.display = 'none';
el.canvas.style.display = 'block';
el.download.disabled = false;
});
el.download.addEventListener('click', () => {
const link = document.createElement('a');
link.download = `photo_${Date.now()}.jpg`;
link.href = el.canvas.toDataURL('image/jpeg', CONFIG.jpegQuality);
link.click();
});
// 清理资源
window.addEventListener('pagehide', () => {
mediaStream?.getTracks().forEach(track => track.stop());
});
// 初始化
initCamera();
})();
</script>
</body>
</html>
本文来自博客园,作者:厚礼蝎,转载请注明原文链接:https://www.cnblogs.com/guangdelw/p/18776823

浙公网安备 33010602011771号