本文详解如何使用
html
5 的 `mediadevices` api 在
浏览器
中安全、兼容地访问移动设备摄像头,列举可用摄像头设备,并提供可直接运行的代码示例与关键注意事项。
在现代 Web 应用中,无需依赖原生 App 即可调用移动设备摄像头——这得益于 HTML5 提供的标准化媒体设备访问能力。核心接口是 navigator.mediaDevices,它属于 MediaDevices API,支持在桌面和移动端(Chrome、Safari(iOS 14.3+)、Firefox、Edge)中获取摄像头、麦克风等硬件列表,并启动实时视频流。
✅ 获取所有可用摄像头(含前后置标识)
⚠️ 注意:enumerateDevices() 不触发权限请求,仅列出已授权或系统预置的设备。若从未授过相机权限,返回的 label 可能为空(如 "videoinput"),需结合 getUserMedia() 触发实际授权。
✅ 直接打开默认/指定摄像头(推荐移动端方案)
对于移动 Web 应用,最可靠的方式是跳过手动选择,直接请求「后置摄像头」或「前置摄像头」:
✅ facingMode: 'environment' 是 iOS 和 Android 主流浏览器(Chrome/Safari/Edge)广泛支持的标准方式,比依赖 deviceId 更健壮。
Comet AI 浏览器
下载 Comet AI 浏览器,体验由 Perplexity AI 驱动的革命性上网方式。内置 AI 助手可实时总结网页、跨标签页对比信息、自动执行任务。告别繁琐操作,让 AI 成为你的浏览副驾,大幅提升研究与工作效率。支持 Windows、macOS、Android 和 iOS。
下载
? 关键注意事项
权限必须由用户显式触发
:getUserMedia() 必须在用户手势(click、tap)回调中调用,否则会被浏览器静默拒绝(尤其在 iOS Safari 中)。
HTTPS 强制要求
:getUserMedia() 在非 HTTPS 环境(如 http://localhost 除外)下完全不可用。
iOS Safari 兼容性
:需添加 playsinline 属性防止全屏播放;autoplay 需配合 muted 才生效;建议设置 width/height 或 CSS object-fit: cover 避免拉伸。
错误处理不可省略
:常见错误包括 NotAllowedError(用户拒绝)、NotFoundError(无可用设备)、NotReadableError(摄像头被占用)。
✅ 总结
你无需“跳转到原生 App”即可实现拍照功能——现代浏览器已原生支持摄像头调用。优先使用 facingMode 控制前后置,辅以 enumerateDevices() 做设备探测与降级提示。搭配合理的权限引导与错误反馈,即可构建体验接近原生的 Web 拍照流程。
async function listCameras() {
try {
// 必须在用户交互(如点击)后调用,否则部分浏览器会拒绝权限
const devices = await navigator.mediaDevices.enumerateDevices();
const videoInputs = devices.filter(device => device.kind === 'videoinput');
console.log('可用摄像头:', videoInputs);
// 示例输出:
// [
// { label: "Front Camera", kind: "videoinput", deviceId: "abc123...", groupId: "..." },
// { label: "Back Camera", kind: "videoinput", deviceId: "def456...", groupId: "..." }
// ]
return videoInputs;
} catch (err) {
console.error('获取设备列表失败:', err.name, err.message);
}
}
const video = document.getElementById('preview');
document.getElementById('openBack').addEventListener('click', async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' }, // 强制后置
audio: false
});
video.srcObject = stream;
} catch (err) {
alert('无法打开后置摄像头:' + err.message);
}
});
document.getElementById('openFront').addEventListener('click', async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'user' }, // 强制前置
audio: false
});
video.srcObject = stream;
} catch (err) {
alert('无法打开前置摄像头:' + err.message);
}
});