From c3da3e937e564c78116721d69c2a2e34fcc13da0 Mon Sep 17 00:00:00 2001 From: lww155 <756794909@qq.com> Date: Mon, 9 Feb 2026 16:50:23 +0800 Subject: [PATCH 1/3] =?UTF-8?q?add:=20=E5=89=8D=E5=90=8E=E6=91=84=E5=83=8F?= =?UTF-8?q?=E5=A4=B4=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/test/css/test_page.css | 27 +++++++++++++ main/xiaozhi-server/test/js/app.js | 42 +++++++++++++++++++- main/xiaozhi-server/test/js/ui/controller.js | 10 +++++ main/xiaozhi-server/test/test_page.html | 9 +++++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/main/xiaozhi-server/test/css/test_page.css b/main/xiaozhi-server/test/css/test_page.css index 1bfc3bd7..9935395a 100644 --- a/main/xiaozhi-server/test/css/test_page.css +++ b/main/xiaozhi-server/test/css/test_page.css @@ -1400,6 +1400,20 @@ body { border-color: rgba(88, 101, 242, 0.5); } +@keyframes flipWithPosition { + 0% { + transform: var(--original-transform) rotateY(0deg); + } + 100% { + transform: var(--original-transform) rotateY(180deg); + } +} + + +.camera-container.flip { + animation: flipWithPosition 0.5s ease-in-out 1 forwards; +} + #cameraVideo { width: 100%; height: 100%; @@ -1407,4 +1421,17 @@ body { background: #1a1a1a; pointer-events: none; transform: scaleX(-1); +} + +.camera-switch { + display: none; + position: fixed; + bottom: 23px; + right: 20px; + z-index: 999; + color: #fff; +} + +.camera-switch.active { + display: block; } \ No newline at end of file diff --git a/main/xiaozhi-server/test/js/app.js b/main/xiaozhi-server/test/js/app.js index bca7e3a7..91f60bf6 100644 --- a/main/xiaozhi-server/test/js/app.js +++ b/main/xiaozhi-server/test/js/app.js @@ -25,6 +25,7 @@ class App { this.audioPlayer = null; this.live2dManager = null; this.cameraStream = null; + this.currentFacingMode = 'user'; } // 初始化应用 @@ -127,6 +128,8 @@ class App { async initCamera() { const cameraContainer = document.getElementById('cameraContainer'); const cameraVideo = document.getElementById('cameraVideo'); + const cameraSwitch = document.getElementById('cameraSwitch'); + const dialBtn = document.getElementById('dialBtn'); if (!cameraContainer || !cameraVideo) { log('摄像头元素未找到,跳过初始化', 'warning'); @@ -188,11 +191,24 @@ class App { } log('正在请求摄像头权限...', 'info'); this.cameraStream = await navigator.mediaDevices.getUserMedia({ - video: { width: 320, height: 240, facingMode: 'user' }, + video: { width: 320, height: 240, facingMode: this.currentFacingMode }, audio: false }); cameraVideo.srcObject = this.cameraStream; + const devices = await navigator.mediaDevices.enumerateDevices(); + const videoDevices = devices.filter(device => device.kind === 'videoinput'); + if (videoDevices.length > 1) { + if (cameraSwitch) cameraSwitch.classList.add('active'); + } cameraContainer.classList.add('active'); + + // 切换时挂断情况 + const hasActive = dialBtn.classList.contains('dial-active'); + if (!hasActive) { + cameraContainer.classList.remove('active'); + cameraSwitch.classList.remove('active'); + window.stopCamera(); + } log('摄像头已启动', 'success'); return true; } catch (error) { @@ -217,6 +233,30 @@ class App { } }; + window.switchCamera = async() => { + if (window.switchCameraTimer) return; + if (this.cameraStream) { + const currentTransform = window.getComputedStyle(cameraContainer).transform; + const originalTransform = currentTransform === 'none' ? 'translate(0px, 0px)' : currentTransform; + cameraContainer.style.setProperty('--original-transform', originalTransform); + cameraContainer.classList.add('flip'); + this.currentFacingMode = this.currentFacingMode === 'user' ? 'environment' : 'user'; + window.stopCamera(); + window.startCamera(); + + window.switchCameraTimer = setTimeout(() => { + if (this.currentFacingMode === 'user') { + cameraVideo.style.transform = 'scaleX(-1)'; + } else { + cameraVideo.style.transform = 'scaleX(1)'; + } + window.switchCameraTimer = null; + cameraContainer.classList.remove('flip'); + cameraContainer.style.removeProperty('--original-transform'); + }, 500); + } + }; + window.takePhoto = (question = '描述一下看到的物品') => { return new Promise(async (resolve) => { const canvas = document.createElement('canvas'); diff --git a/main/xiaozhi-server/test/js/ui/controller.js b/main/xiaozhi-server/test/js/ui/controller.js index bc23f298..947783a3 100644 --- a/main/xiaozhi-server/test/js/ui/controller.js +++ b/main/xiaozhi-server/test/js/ui/controller.js @@ -98,6 +98,14 @@ class UIController { }); } + // Camera switch button + const cameraSwitch = document.getElementById('cameraSwitch'); + if (cameraSwitch) { + cameraSwitch.addEventListener('click', () => { + window.switchCamera(); + }) + } + // Dial button const dialBtn = document.getElementById('dialBtn'); if (dialBtn) { @@ -115,6 +123,7 @@ class UIController { if (isConnected) { wsHandler.disconnect(); this.updateDialButton(false); + if (cameraSwitch) cameraSwitch.classList.remove('active'); this.addChatMessage('Disconnected, see you next time~😊', false); } else { // Check if OTA URL is filled @@ -153,6 +162,7 @@ class UIController { if (isActive) { // 关闭摄像头 if (typeof window.stopCamera === 'function') { + if (cameraSwitch) cameraSwitch.classList.remove('active'); window.stopCamera(); } cameraContainer.classList.remove('active'); diff --git a/main/xiaozhi-server/test/test_page.html b/main/xiaozhi-server/test/test_page.html index 22d4b0f6..7f85a127 100644 --- a/main/xiaozhi-server/test/test_page.html +++ b/main/xiaozhi-server/test/test_page.html @@ -44,6 +44,15 @@
+ + +
+ + + +
+
From b22f430e982a4324acf11e7ad53ef462058124f3 Mon Sep 17 00:00:00 2001 From: rainv123 <2148537152@qq.com> Date: Mon, 9 Feb 2026 17:22:28 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=E8=B0=83=E6=95=B4=E6=91=84=E5=83=8F?= =?UTF-8?q?=E5=A4=B4=E6=98=BE=E7=A4=BA=E5=8C=BA=E5=9F=9F=E4=B8=BA=E7=AB=96?= =?UTF-8?q?=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/test/css/test_page.css | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/main/xiaozhi-server/test/css/test_page.css b/main/xiaozhi-server/test/css/test_page.css index 9935395a..17d00b00 100644 --- a/main/xiaozhi-server/test/css/test_page.css +++ b/main/xiaozhi-server/test/css/test_page.css @@ -1377,8 +1377,8 @@ body { position: fixed; top: 20px; left: 20px; - width: 240px; - height: 180px; + width: 180px; + height: 240px; background: rgba(0, 0, 0, 0.5); backdrop-filter: blur(10px); border-radius: 8px; @@ -1402,10 +1402,11 @@ body { @keyframes flipWithPosition { 0% { - transform: var(--original-transform) rotateY(0deg); + transform: var(--original-transform) rotateY(0deg); } + 100% { - transform: var(--original-transform) rotateY(180deg); + transform: var(--original-transform) rotateY(180deg); } } @@ -1425,10 +1426,10 @@ body { .camera-switch { display: none; - position: fixed; - bottom: 23px; - right: 20px; - z-index: 999; + position: fixed; + bottom: 23px; + right: 20px; + z-index: 999; color: #fff; } From 85fab6458882a8e9372b9f497d7e8b20c694292f Mon Sep 17 00:00:00 2001 From: lww155 <756794909@qq.com> Date: Tue, 10 Feb 2026 09:54:39 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96h5=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/test/css/test_page.css | 301 +++++++++++---------- 1 file changed, 157 insertions(+), 144 deletions(-) diff --git a/main/xiaozhi-server/test/css/test_page.css b/main/xiaozhi-server/test/css/test_page.css index 17d00b00..fb51457f 100644 --- a/main/xiaozhi-server/test/css/test_page.css +++ b/main/xiaozhi-server/test/css/test_page.css @@ -1201,150 +1201,6 @@ body { z-index: 999; } -/* ==================== 响应式设计 ==================== */ -@media (max-width: 768px) { - .container { - padding: 10px; - } - - .live2d-section { - height: 60vh; - margin-bottom: 10px; - } - - .live2d-canvas { - max-width: 300px; - max-height: 450px; - } - - .control-bar { - bottom: 10px; - padding: 8px 16px; - gap: 12px; - } - - .control-btn { - width: 50px; - height: 50px; - } - - .btn-icon { - width: 20px; - height: 20px; - } - - .btn-text { - font-size: 10px; - } - - .chat-container { - width: 230px; - right: 10px; - bottom: 105px; - } - - .message-bubble { - max-width: 200px; - font-size: 12px; - } - - .modal-content { - width: 95%; - margin: 10px; - } - - .config-row { - flex-direction: column; - gap: 12px; - } - - .audio-visualizer { - width: 150px; - height: 50px; - left: 10px; - bottom: 80px; - } -} - -@media (max-width: 480px) { - .live2d-section { - height: 50vh; - } - - .live2d-canvas { - max-width: 250px; - max-height: 350px; - } - - .control-bar { - gap: 8px; - padding: 6px 12px; - } - - .control-btn { - width: 45px; - height: 45px; - } - - .btn-text { - display: none; - } - - .chat-stream { - width: 200px; - max-height: 150px; - } - - .audio-visualizer { - width: 120px; - height: 40px; - left: 5px; - bottom: 70px; - } -} - -/* 竖屏优化 */ -@media (max-height: 700px) { - .live2d-section { - height: 55vh; - } - - .live2d-canvas { - max-height: 400px; - } - - .chat-stream { - bottom: 70px; - max-height: 150px; - } - - .audio-visualizer { - bottom: 70px; - } -} - -/* 横屏优化 */ -@media (min-width: 1024px) and (min-height: 600px) { - .live2d-section { - height: 75vh; - } - - .live2d-canvas { - max-width: 500px; - max-height: 650px; - } - - .chat-stream { - width: 350px; - max-height: 250px; - } - - .audio-visualizer { - width: 250px; - height: 80px; - } -} - /* 滚动条样式 */ .chat-stream::-webkit-scrollbar, .modal-body::-webkit-scrollbar, @@ -1435,4 +1291,161 @@ body { .camera-switch.active { display: block; +} + +/* ==================== 响应式设计 ==================== */ +@media (max-width: 768px) { + .container { + padding: 10px; + } + + .live2d-section { + height: 60vh; + margin-bottom: 10px; + } + + .live2d-canvas { + max-width: 300px; + max-height: 450px; + } + + .control-bar { + bottom: 10px; + padding: 8px 16px; + gap: 12px; + } + + .control-btn { + width: 50px; + height: 50px; + } + + .btn-icon { + width: 20px; + height: 20px; + } + + .btn-text { + font-size: 10px; + } + + .chat-container { + width: 230px; + right: 10px; + bottom: 105px; + } + + .message-bubble { + max-width: 200px; + font-size: 12px; + } + + .modal-content { + width: 95%; + margin: 10px; + } + + .config-row { + flex-direction: column; + gap: 12px; + } + + .audio-visualizer { + width: 150px; + height: 50px; + left: 10px; + bottom: 80px; + } + + .chat-ipt input { + font-size: 12px; + } + + .camera-container { + width: 130px; + height: 180px; + } + + .status-indicator { + font-size: 12px; + } +} + +@media (max-width: 480px) { + .live2d-section { + height: 50vh; + } + + .live2d-canvas { + max-width: 250px; + max-height: 350px; + } + + .control-bar { + gap: 8px; + padding: 6px 12px; + } + + .control-btn { + width: 45px; + height: 45px; + } + + .btn-text { + display: none; + } + + .chat-stream { + width: 200px; + max-height: 150px; + } + + .audio-visualizer { + width: 120px; + height: 40px; + left: 5px; + bottom: 70px; + } +} + +/* 竖屏优化 */ +@media (max-height: 700px) { + .live2d-section { + height: 55vh; + } + + .live2d-canvas { + max-height: 400px; + } + + .chat-stream { + bottom: 70px; + max-height: 150px; + } + + .audio-visualizer { + bottom: 70px; + } +} + +/* 横屏优化 */ +@media (min-width: 1024px) and (min-height: 600px) { + .live2d-section { + height: 75vh; + } + + .live2d-canvas { + max-width: 500px; + max-height: 650px; + } + + .chat-stream { + width: 350px; + max-height: 250px; + } + + .audio-visualizer { + width: 250px; + height: 80px; + } } \ No newline at end of file