前端实现自定义范围拍摄
约 798 字大约 3 分钟
2026-04-19
在项目开发时,遇到了拍摄身份证照的需求,需要前端对相机拍摄自定义,但前端调用相机一般是通过 input=file 调用系统相机,那这种自定义拍摄又是如何实现的呢?
核心原理
- 媒体流获取:通过 navigator.mediaDevices.getUserMedia() 请求用户授权,获取摄像头 / 麦克风的实时媒体流(MediaStream)。
- 实时预览:将媒体流赋值给
<video>元素的 srcObject 属性,实现画面实时预览。 - 图像 / 视频捕获:
- 拍照:利用
<canvas>的 drawImage() 方法将<video>当前帧绘制到画布,再通过 toDataURL() 或 toBlob() 导出图像。 - 录像:使用 MediaRecorder API 录制媒体流,生成视频文件。
- 拍照:利用
功能实现(代码示例)
DOM结构
<template>
<div class="id-card-camera">
<!-- 准备界面 -->
<div
v-if="!isShooting && !capturedImage"
class="normal-container"
>
<!-- 示例图片展示 -->
</div>
<!-- 拍摄界面 -->
<div
v-else-if="isShooting"
class="camera-container"
>
<!-- 实时视频预览 -->
<video
ref="video"
...
></video>
<!-- 身份证定位框 -->
</div>
<!-- 预览界面 -->
<div
v-else-if="capturedImage"
class="preview-container"
>
<!-- 拍摄结果预览 -->
</div>
</div>
</template>数据模型
data() {
return {
isShooting: false, // 拍摄状态
capturedImage: null, // 拍摄的图片数据
videoWidth: 390, // 视频宽度
videoHeight: 214, // 视频高度
stream: null, // 媒体流对象
tips: { // 拍摄提示信息
'身份证人像面': [...],
'身份证国徽面': [...]
}
}
}1. 摄像头初始化
async initCamera() {
try {
// 请求摄像头权限,使用后置摄像头
this.stream = await navigator.mediaDevices.getUserMedia({
video: {
width: this.videoWidth * 7, // 高分辨率设置
height: this.videoHeight * 7,
facingMode: 'environment' // 后置摄像头
},
audio: false // 禁用音频
});
// 绑定视频流到video元素
const video = this.$refs.video;
if (video) {
video.srcObject = this.stream;
video.onloadedmetadata = function() {
video.play();
};
}
} catch (error) {
console.error('无法访问摄像头:', error);
alert('无法访问摄像头,请确保已授予摄像头权限');
}
}关键点:
facingMode: 'environment'强制使用后置摄像头- 分辨率设置为显示尺寸的 7 倍,确保高质量截图
- 完整的错误处理和用户提示
2. 精准图像裁剪算法
captureFromCamera() {
const video = this.$refs.video;
// 1. 获取摄像头真实分辨率
const videoRealWidth = video.videoWidth; // 如3264px
const videoRealHeight = video.videoHeight; // 如2448px
// 2. 目标裁剪尺寸(与显示框一致)
const targetWidth = this.videoWidth; // 390px
const targetHeight = this.videoHeight; // 214px
// 3. 计算缩放比例和裁剪区域
const ratio = Math.min(
videoRealWidth / targetWidth,
videoRealHeight / targetHeight
);
const cropWidth = targetWidth * ratio; // 实际裁剪宽度
const cropHeight = targetHeight * ratio; // 实际裁剪高度
// 4. 计算居中裁剪起始点
const cropX = (videoRealWidth - cropWidth) / 2;
const cropY = (videoRealHeight - cropHeight) / 2;
// 5. Canvas精准裁剪
const canvas = document.createElement('canvas');
canvas.width = cropWidth;
canvas.height = cropHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(
video,
cropX, cropY, cropWidth, cropHeight, // 源区域
0, 0, cropWidth, cropHeight // 目标区域
);
// 6. 生成高质量JPEG图片
this.capturedImage = canvas.toDataURL('image/jpeg', 1);
this.isShooting = false;
}3. 资源管理
// 关闭摄像头,释放资源
closeCamera() {
if (this.stream) {
this.stream.getTracks().forEach(track => {
track.stop();
});
this.stream = null;
}
this.isShooting = false;
this.capturedImage = null;
}注意事项
- HTTPS 限制:getUserMedia 仅在 HTTPS 或 localhost 环境下可用,部署时需注意。
- 分辨率设置,录制是是物理像素,如果需要提升清晰度,需要将尺寸放大,同时用 scale 进行缩放。