丸子 5 ay önce
ebeveyn
işleme
672608d07b
2 değiştirilmiş dosya ile 241 ekleme ve 1 silme
  1. 2 1
      src/router/index.js
  2. 239 0
      src/views/CameraDemo.vue

+ 2 - 1
src/router/index.js

@@ -5,6 +5,8 @@ import App from '../App.vue'
 const routes = [
 
   { path: '/', component: () => import('../views/Login.vue') },
+  // 相机Demo页面
+  { path: '/camera-demo', component: () => import('../views/CameraDemo.vue') },
   { path: '/login', component: () => import('../views/Login.vue') },
   // 免登录
   { path: '/quick-login', component: () => import('../views/QuickLogin.vue') },
@@ -90,7 +92,6 @@ const routes = [
   { path: '/mapGame', component: () => import('../views/block/MapGame.vue') },
   // 编程游戏列表
   { path: '/gamepage', component: () => import('../views/gamepage/GameIndex.vue') },
-
 ]
 const router = createRouter({
   history: createWebHistory(),

+ 239 - 0
src/views/CameraDemo.vue

@@ -0,0 +1,239 @@
+<template>
+  <div class="camera-container">
+    <h1>相机Demo</h1>
+    
+    <div class="camera-section">
+      <!-- 视频元素,用于捕获相机流但不直接显示 -->
+      <video ref="videoElement" autoplay playsinline style="display: none;"></video>
+      
+      <!-- Canvas元素,用于渲染相机画面 -->
+      <canvas ref="canvasElement" class="camera-canvas"></canvas>
+    </div>
+    
+    <div class="controls">
+      <button @click="startCamera" v-if="!isCameraActive">启动相机</button>
+      <button @click="stopCamera" v-if="isCameraActive">停止相机</button>
+      <button @click="takePhoto" v-if="isCameraActive">拍照</button>
+    </div>
+    
+    <div class="photo-preview" v-if="capturedImage">
+      <h3>拍摄预览</h3>
+      <img :src="capturedImage" alt="拍摄的照片" class="preview-image">
+      <button @click="savePhoto">保存照片</button>
+      <button @click="clearPhoto">清除</button>
+    </div>
+    
+    <div class="status" v-if="statusMessage">
+      {{ statusMessage }}
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+
+// 引用DOM元素
+const videoElement = ref(null);
+const canvasElement = ref(null);
+const canvasContext = ref(null);
+
+// 状态变量
+const isCameraActive = ref(false);
+const capturedImage = ref(null);
+const statusMessage = ref('');
+let animationId = null;
+
+// 组件挂载时初始化canvas
+onMounted(() => {
+  if (canvasElement.value) {
+    canvasContext.value = canvasElement.value.getContext('2d');
+  }
+});
+
+// 启动相机
+const startCamera = async () => {
+  try {
+    statusMessage.value = '正在请求相机权限...';
+    
+    // 请求相机权限并获取视频流
+    const stream = await navigator.mediaDevices.getUserMedia({
+      video: {
+        width: { ideal: 1280 },
+        height: { ideal: 720 }
+      },
+      audio: false
+    });
+    
+    // 将视频流设置到视频元素
+    if (videoElement.value) {
+      videoElement.value.srcObject = stream;
+      isCameraActive.value = true;
+      statusMessage.value = '相机已启动';
+      
+      // 等待视频元素准备就绪
+      videoElement.value.onloadedmetadata = () => {
+        // 设置canvas尺寸与视频尺寸匹配
+        if (canvasElement.value && canvasContext.value) {
+          canvasElement.value.width = videoElement.value.videoWidth;
+          canvasElement.value.height = videoElement.value.videoHeight;
+          // 开始渲染视频到canvas
+          startRendering();
+        }
+      };
+    }
+  } catch (error) {
+    console.error('相机访问错误:', error);
+    statusMessage.value = `相机访问失败: ${error.message}`;
+  }
+};
+
+// 渲染视频到canvas
+const startRendering = () => {
+  if (!videoElement.value || !canvasElement.value || !canvasContext.value) {
+    return;
+  }
+  
+  const render = () => {
+    if (videoElement.value && canvasContext.value) {
+      // 将视频帧绘制到canvas
+      canvasContext.value.drawImage(
+        videoElement.value,
+        0, 0,
+        canvasElement.value.width,
+        canvasElement.value.height
+      );
+      
+      // 继续下一帧渲染
+      animationId = requestAnimationFrame(render);
+    }
+  };
+  
+  // 开始渲染循环
+  animationId = requestAnimationFrame(render);
+};
+
+// 停止相机
+const stopCamera = () => {
+  if (videoElement.value && videoElement.value.srcObject) {
+    // 停止渲染循环
+    if (animationId) {
+      cancelAnimationFrame(animationId);
+      animationId = null;
+    }
+    
+    // 停止所有视频轨道
+    const tracks = videoElement.value.srcObject.getTracks();
+    tracks.forEach(track => track.stop());
+    videoElement.value.srcObject = null;
+    
+    isCameraActive.value = false;
+    statusMessage.value = '相机已停止';
+  }
+};
+
+// 拍照功能
+const takePhoto = () => {
+  if (canvasElement.value && canvasContext.value) {
+    // 将canvas内容转换为图片URL
+    capturedImage.value = canvasElement.value.toDataURL('image/png');
+    statusMessage.value = '已拍照';
+  }
+};
+
+// 保存照片功能
+const savePhoto = () => {
+  if (capturedImage.value) {
+    // 创建下载链接
+    const link = document.createElement('a');
+    link.href = capturedImage.value;
+    // 设置文件名,使用时间戳确保唯一性
+    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
+    link.download = `camera-photo-${timestamp}.png`;
+    // 触发下载
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+    statusMessage.value = '照片已保存';
+  }
+};
+
+// 清除照片预览
+const clearPhoto = () => {
+  capturedImage.value = null;
+  statusMessage.value = '';
+};
+
+// 组件卸载时清理资源
+onUnmounted(() => {
+  stopCamera();
+});
+</script>
+
+<style scoped>
+.camera-container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 20px;
+  text-align: center;
+}
+
+.camera-section {
+  margin: 20px 0;
+  position: relative;
+  display: inline-block;
+}
+
+.camera-canvas {
+  border: 2px solid #ccc;
+  border-radius: 8px;
+  max-width: 100%;
+  background-color: #f0f0f0;
+}
+
+.controls {
+  margin: 20px 0;
+}
+
+button {
+  padding: 10px 20px;
+  margin: 0 10px;
+  font-size: 16px;
+  background-color: #4CAF50;
+  color: white;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: background-color 0.3s;
+}
+
+button:hover {
+  background-color: #45a049;
+}
+
+button:active {
+  background-color: #3e8e41;
+}
+
+.photo-preview {
+  margin-top: 30px;
+  padding: 20px;
+  border: 1px solid #ddd;
+  border-radius: 8px;
+  background-color: #f9f9f9;
+}
+
+.preview-image {
+  max-width: 100%;
+  max-height: 400px;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+}
+
+.status {
+  margin-top: 20px;
+  padding: 10px;
+  background-color: #f0f0f0;
+  border-radius: 4px;
+  color: #333;
+}
+</style>