|
@@ -63,6 +63,13 @@
|
|
|
<div class="camera-container">
|
|
<div class="camera-container">
|
|
|
<div v-if="!isCapturing" class="camera-view">
|
|
<div v-if="!isCapturing" class="camera-view">
|
|
|
<video ref="videoRef" autoplay muted playsinline></video>
|
|
<video ref="videoRef" autoplay muted playsinline></video>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ class="switch-camera-btn"
|
|
|
|
|
+ @click="switchCamera"
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ :icon="Camera"
|
|
|
|
|
+ circle
|
|
|
|
|
+ ></el-button>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-else class="captured-image-view">
|
|
<div v-else class="captured-image-view">
|
|
|
<img :src="capturedImage" alt="已拍摄照片" />
|
|
<img :src="capturedImage" alt="已拍摄照片" />
|
|
@@ -112,6 +119,7 @@ const capturedImage = ref(''); // 拍摄的照片
|
|
|
const isCapturing = ref(false); // 是否正在拍摄中
|
|
const isCapturing = ref(false); // 是否正在拍摄中
|
|
|
const videoRef = ref(null); // 视频元素引用
|
|
const videoRef = ref(null); // 视频元素引用
|
|
|
const isSmallScreen = ref(false); // 是否为小屏设备
|
|
const isSmallScreen = ref(false); // 是否为小屏设备
|
|
|
|
|
+const currentCamera = ref('environment'); // 当前使用的摄像头方向 (environment: 后摄, user: 前摄)
|
|
|
|
|
|
|
|
// 监听窗口大小变化
|
|
// 监听窗口大小变化
|
|
|
const handleResize = () => {
|
|
const handleResize = () => {
|
|
@@ -233,8 +241,8 @@ const download = (index) => {
|
|
|
document.body.removeChild(link);
|
|
document.body.removeChild(link);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 打开相机弹窗
|
|
|
|
|
-const openCamera = async () => {
|
|
|
|
|
|
|
+// 获取相机视频流的辅助函数
|
|
|
|
|
+const getCameraStream = async (facingMode) => {
|
|
|
try {
|
|
try {
|
|
|
// 获取相机容器的尺寸
|
|
// 获取相机容器的尺寸
|
|
|
const containerWidth = window.innerWidth * (isSmallScreen.value ? 0.9 : 0.8);
|
|
const containerWidth = window.innerWidth * (isSmallScreen.value ? 0.9 : 0.8);
|
|
@@ -247,24 +255,45 @@ const openCamera = async () => {
|
|
|
frameRate: { ideal: 30, max: 60 }
|
|
frameRate: { ideal: 30, max: 60 }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- // 尝试优先使用后摄像头(不带exact,提高兼容性)
|
|
|
|
|
|
|
+ // 尝试获取指定方向的摄像头
|
|
|
|
|
+ return await navigator.mediaDevices.getUserMedia({
|
|
|
|
|
+ video: { ...videoConstraints, facingMode }
|
|
|
|
|
+ });
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error(`获取${facingMode === 'environment' ? '后' : '前'}摄像头失败:`, error);
|
|
|
|
|
+ throw error;
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 打开相机弹窗
|
|
|
|
|
+const openCamera = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 尝试获取后摄像头(默认)
|
|
|
try {
|
|
try {
|
|
|
- stream.value = await navigator.mediaDevices.getUserMedia({
|
|
|
|
|
- video: { ...videoConstraints, facingMode: 'environment' }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ stream.value = await getCameraStream('environment');
|
|
|
|
|
+ currentCamera.value = 'environment';
|
|
|
} catch (backCameraError) {
|
|
} catch (backCameraError) {
|
|
|
console.log('后摄像头不可用,尝试使用前摄像头:', backCameraError);
|
|
console.log('后摄像头不可用,尝试使用前摄像头:', backCameraError);
|
|
|
// 后摄像头不可用,尝试使用前摄像头
|
|
// 后摄像头不可用,尝试使用前摄像头
|
|
|
try {
|
|
try {
|
|
|
- stream.value = await navigator.mediaDevices.getUserMedia({
|
|
|
|
|
- video: { ...videoConstraints, facingMode: 'user' }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ stream.value = await getCameraStream('user');
|
|
|
|
|
+ currentCamera.value = 'user';
|
|
|
} catch (frontCameraError) {
|
|
} catch (frontCameraError) {
|
|
|
console.log('前摄像头也不可用,尝试默认设置:', frontCameraError);
|
|
console.log('前摄像头也不可用,尝试默认设置:', frontCameraError);
|
|
|
// 前摄像头也不可用,使用默认设置
|
|
// 前摄像头也不可用,使用默认设置
|
|
|
|
|
+ const containerWidth = window.innerWidth * (isSmallScreen.value ? 0.9 : 0.8);
|
|
|
|
|
+ const containerHeight = window.innerHeight * (isSmallScreen.value ? 0.7 : 0.6);
|
|
|
|
|
+
|
|
|
|
|
+ const videoConstraints = {
|
|
|
|
|
+ width: { ideal: containerWidth, max: containerWidth * 1.2 },
|
|
|
|
|
+ height: { ideal: containerHeight, max: containerHeight * 1.2 },
|
|
|
|
|
+ frameRate: { ideal: 30, max: 60 }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
stream.value = await navigator.mediaDevices.getUserMedia({
|
|
stream.value = await navigator.mediaDevices.getUserMedia({
|
|
|
video: videoConstraints
|
|
video: videoConstraints
|
|
|
});
|
|
});
|
|
|
|
|
+ currentCamera.value = 'default';
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -284,6 +313,50 @@ const openCamera = async () => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+// 切换摄像头
|
|
|
|
|
+const switchCamera = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 停止当前的媒体流
|
|
|
|
|
+ if (stream.value) {
|
|
|
|
|
+ stream.value.getTracks().forEach(track => track.stop());
|
|
|
|
|
+ stream.value = null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 切换摄像头方向
|
|
|
|
|
+ const newCamera = currentCamera.value === 'environment' ? 'user' : 'environment';
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 尝试获取新的摄像头流
|
|
|
|
|
+ stream.value = await getCameraStream(newCamera);
|
|
|
|
|
+ currentCamera.value = newCamera;
|
|
|
|
|
+
|
|
|
|
|
+ // 更新视频源
|
|
|
|
|
+ if (videoRef.value) {
|
|
|
|
|
+ videoRef.value.srcObject = stream.value;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ // 如果获取新摄像头失败,尝试切换回原来的摄像头
|
|
|
|
|
+ console.error(`切换到${newCamera === 'environment' ? '后' : '前'}摄像头失败:`, error);
|
|
|
|
|
+ Message().error(`无法切换到${newCamera === 'environment' ? '后' : '前'}摄像头,请检查设备是否支持!`, true);
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试恢复原来的摄像头
|
|
|
|
|
+ try {
|
|
|
|
|
+ stream.value = await getCameraStream(currentCamera.value);
|
|
|
|
|
+ if (videoRef.value) {
|
|
|
|
|
+ videoRef.value.srcObject = stream.value;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (revertError) {
|
|
|
|
|
+ console.error('恢复原来摄像头失败:', revertError);
|
|
|
|
|
+ Message().error('无法恢复原来的摄像头!', true);
|
|
|
|
|
+ closeCamera();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('切换摄像头失败:', error);
|
|
|
|
|
+ Message().error('切换摄像头失败,请重试!', true);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
// 关闭相机弹窗
|
|
// 关闭相机弹窗
|
|
|
const closeCamera = () => {
|
|
const closeCamera = () => {
|
|
|
if (stream.value) {
|
|
if (stream.value) {
|
|
@@ -514,6 +587,16 @@ const confirmAndUpload = async () => {
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.camera-view {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.camera-view video {
|
|
.camera-view video {
|
|
|
max-width: 100%;
|
|
max-width: 100%;
|
|
|
max-height: 100%;
|
|
max-height: 100%;
|
|
@@ -522,6 +605,29 @@ const confirmAndUpload = async () => {
|
|
|
border-radius: 5px;
|
|
border-radius: 5px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.switch-camera-btn {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ bottom: 20px;
|
|
|
|
|
+ right: 20px;
|
|
|
|
|
+ background-color: rgba(0, 0, 0, 0.5);
|
|
|
|
|
+ color: white;
|
|
|
|
|
+ font-size: 20px;
|
|
|
|
|
+ width: 40px;
|
|
|
|
|
+ height: 40px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ z-index: 10;
|
|
|
|
|
+ transition: background-color 0.3s;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.switch-camera-btn:hover {
|
|
|
|
|
+ background-color: rgba(0, 0, 0, 0.7);
|
|
|
|
|
+ color: white;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.captured-image-view {
|
|
.captured-image-view {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
height: 100%;
|
|
height: 100%;
|