主要流程
视频拍照功能需要拆分成视频 / 拍照 ↓
视频使用video标签配合煤气输入完成,拍照使用canvas标签生成图片 ↓
在点击拍照的时候获取到video当前容器对象绘制一张图片,将图片存储为file/blob/base64都行
1.开启摄像头(注:如果当前摄像头在工作中是需要关闭正在工作中的摄像头,要不然无法开启)
const MediaStreamTrack = ref([]);
// 开启摄像头:tagRef是video元素
function openCamera(tagRef: any) {
console.log('正在启动摄像头')
// 存储跟踪列表,在关闭摄像头的时候使用
return new Promise((resolve, reject) => {
// 检查设备是否支持摄像头
navigator.mediaDevices.enumerateDevices().then((devicesList) => {
const kindList = devicesList.map((item) => item.kind);
if (!kindList.includes("videoinput")) {
feedback.msgError('系统不支持摄像操作')
reject("系统不支持摄像操作");
} else {
// 开启摄像头操作
navigator.mediaDevices
.getUserMedia({ video: true })
.then(function (stream) {
/* 使用这个 stream 传递到成功回调中 */
tagRef.srcObject = stream;
tagRef.play();
// 存储摄像头的跟踪信息(需要定义一个MediaStreamTrack的ref变量)
MediaStreamTrack.value = stream.getTracks() as any;
})
.catch(function (err) {
/* 处理 error 信息 */
reject(err);
});
}
});
});
}
2.拍照(canvas可以转换成很多种方式,blob/base64/file,根据自身情况去选择)
// 拍摄:videoRef是viedo元素,canvasRef是canvas元素
function capture(videoRef: any, canvasRef: any) {
return new Promise((resolve, reject) => {
// 绘制画布
context = canvasRef.getContext("2d");
try {
var radius = 8;
context.beginPath();
// 左上角
context.arc(radius, radius, radius, Math.PI, Math.PI * 1.5);
// 右上角
context.arc(
videoRef.offsetWidth - radius,
radius,
radius,
Math.PI * 1.5,
Math.PI * 2
);
// 右下角
context.arc(
videoRef.offsetWidth - radius,
videoRef.offsetHeight - radius,
radius,
0,
Math.PI * 0.5
);
// 左下角
context.arc(
radius,
videoRef.offsetHeight - radius,
radius,
Math.PI * 0.5,
Math.PI
);
// 结束绘制路径
context.closePath();
// 将路径限制到绘图区域内
context.clip();
// 不需要圆角的话可以忽略上面这一段
/* 要跟video的宽高一致 */
context.drawImage(
videoRef,
0,
0,
videoRef.offsetWidth,
videoRef.offsetHeight
);
// 将cavans对象转化为Blod对象
canvasRef.toBlob(
(blob: Blob) => {
const formData = new FormData()
formData.append('file', blob)
resolve(formData.get('file'));
},
"image/png/webp",
1
);
} catch (err) {
reject("图片截取失败" + err);
}
});
}
3.关闭摄像头(在离开页面后需要关闭摄像头)
// 关闭摄像头
function closeCamera(videoRef: any) {
if (MediaStreamTrack.value.length) {
MediaStreamTrack.value.forEach((track: { stop: () => void }) => {
track.stop();
});
videoRef.srcObject = null;
}
}
4.完整hook文件
import { ref } from "vue";
// 这一块是封装的消息提示文件
import feedback from "@/utils/feedback";
export default () => {
const MediaStreamTrack = ref([]);
let context: any = null;
// 开启摄像头
function openCamera(tagRef: any) {
feedback.notify('正在启动摄像头')
// 存储跟踪列表,在关闭摄像头的时候使用
return new Promise((resolve, reject) => {
// 检查设备是否支持摄像头
navigator.mediaDevices.enumerateDevices().then((devicesList) => {
const kindList = devicesList.map((item) => item.kind);
if (!kindList.includes("videoinput")) {
feedback.msgError('系统不支持摄像操作')
reject("系统不支持摄像操作");
} else {
// 开启摄像头操作
navigator.mediaDevices
.getUserMedia({ video: true })
.then(function (stream) {
/* 使用这个 stream 传递到成功回调中 */
tagRef.srcObject = stream;
// 存储摄像头的跟踪信息
tagRef.play();
MediaStreamTrack.value = stream.getTracks() as any;
})
.catch(function (err) {
/* 处理 error 信息 */
feedback.msgError(err)
reject(err);
});
}
});
});
}
// 拍摄
function capture(videoRef: any, canvasRef: any) {
return new Promise((resolve, reject) => {
// 绘制画布
context = canvasRef.getContext("2d");
try {
/* 要跟video的宽高一致 */
var radius = 8;
context.beginPath();
// 左上角
context.arc(radius, radius, radius, Math.PI, Math.PI * 1.5);
// 右上角
context.arc(
videoRef.offsetWidth - radius,
radius,
radius,
Math.PI * 1.5,
Math.PI * 2
);
// 右下角
context.arc(
videoRef.offsetWidth - radius,
videoRef.offsetHeight - radius,
radius,
0,
Math.PI * 0.5
);
// 左下角
context.arc(
radius,
videoRef.offsetHeight - radius,
radius,
Math.PI * 0.5,
Math.PI
);
// 结束绘制路径
context.closePath();
// 将路径限制到绘图区域内
context.clip();
context.drawImage(
videoRef,
0,
0,
videoRef.offsetWidth,
videoRef.offsetHeight
);
// 将cavans对象转化为Blod对象
canvasRef.toBlob(
(blob: Blob) => {
const formData = new FormData()
formData.append('file', blob)
resolve(formData.get('file'));
},
"image/png/webp",
1
);
} catch (err) {
reject("图片截取失败" + err);
}
});
}
// 关闭摄像头
function closeCamera(videoRef: any) {
if (MediaStreamTrack.value.length) {
MediaStreamTrack.value.forEach((track: { stop: () => void }) => {
track.stop();
});
videoRef.srcObject = null;
}
}
return {
openCamera,
capture,
closeCamera
};
};
5.页面中使用(大致的使用方式就是如下代码,根据不同情况调整就好)
<video
ref="videoRef"
class="video-item"
></video>
<canvas
class="cavcas-img"
ref="canvasRef"
></canvas>
// 引入useCamera
import useCamera from "@/hooks/useCamera";
// 调用userCamera
const { openCamera, capture, closeCamera } = useCamera();
// 开启摄像头
const openVideo = () => {
openCamera(videoRef.value);
};
// 拍照
const videoClick = async () => {
const rews = await (await capture(videoRef.value, canvasRef.value)) as FormData;
console.log(rews);
};
onBeforeUnmount(() => {
// 关闭摄像头
closeCamera(videoRef.value);
});