mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
Merge pull request #2839 from xinnan-tech/fix-digital-human
修复背景404、通话接通后自动录音、模型加载Loading
This commit is contained in:
@@ -54,6 +54,82 @@ body {
|
||||
/* 移除底部padding,让模型可以延伸到页面底部 */
|
||||
}
|
||||
|
||||
/* ==================== 模型加载区 ==================== */
|
||||
.model-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
transform: translate(-50%, -50%);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.model-loading {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 999;
|
||||
transition: opacity 0.5s ease-out, visibility 0.5s ease-out;
|
||||
background-image: linear-gradient(transparent 50%, rgba(88, 101, 242, 0.05) 50%);
|
||||
background-size: 100% 4px;
|
||||
animation: scan 1.26s linear infinite;
|
||||
}
|
||||
|
||||
.loading-char-float {
|
||||
font-size: 22px;
|
||||
letter-spacing: 3px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loading-char-float .main-char {
|
||||
color: #f0f8ff;
|
||||
text-shadow: 0 0 5px #5865f2, 0 0 10px rgba(88, 101, 242, 0.8);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.loading-char-float .main-char span {
|
||||
animation: char-float 1.26s ease-in-out infinite;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.loading-char-float .shadow-char {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 0;
|
||||
color: rgba(88, 101, 242, 0.3);
|
||||
pointer-events: none;
|
||||
animation: shadow-float 1.26s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.loading-char-float .main-char span:nth-child(1) { animation-delay: 0s; }
|
||||
.loading-char-float .main-char span:nth-child(2) { animation-delay: 0.11s; }
|
||||
.loading-char-float .main-char span:nth-child(3) { animation-delay: 0.22s; }
|
||||
.loading-char-float .main-char span:nth-child(4) { animation-delay: 0.33s; }
|
||||
.loading-char-float .main-char span:nth-child(5) { animation-delay: 0.44s; }
|
||||
.loading-char-float .main-char span:nth-child(6) { animation-delay: 0.55s; }
|
||||
|
||||
@keyframes scan {
|
||||
0% { background-position: 0 0; }
|
||||
100% { background-position: 0 100px; }
|
||||
}
|
||||
|
||||
@keyframes char-float {
|
||||
0%, 100% { transform: translateY(0); opacity: 1; text-shadow: 0 0 5px #5865f2, 0 0 10px rgba(88, 101, 242, 0.8); }
|
||||
50% { transform: translateY(-5px); opacity: 0.9; text-shadow: 0 0 10px #5865f2, 0 0 20px rgba(88, 101, 242, 0.8); }
|
||||
}
|
||||
|
||||
@keyframes shadow-float {
|
||||
0%, 100% { transform: translateY(3px); opacity: 0.3; }
|
||||
50% { transform: translateY(8px); opacity: 0.15; }
|
||||
}
|
||||
|
||||
/* ==================== Live2D显示区域 ==================== */
|
||||
#live2d-stage {
|
||||
position: fixed;
|
||||
|
||||
@@ -37,9 +37,20 @@ class App {
|
||||
// 初始化Live2D
|
||||
await this.initLive2D();
|
||||
|
||||
// 关闭加载loading
|
||||
this.setModelLoadingStatus(false);
|
||||
|
||||
log('应用初始化完成', 'success');
|
||||
}
|
||||
|
||||
// 设置model加载状态
|
||||
setModelLoadingStatus(isLoading) {
|
||||
const modelLoading = document.getElementById('modelLoading');
|
||||
if (modelLoading) {
|
||||
modelLoading.style.display = isLoading ? 'flex' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化Live2D
|
||||
async initLive2D() {
|
||||
try {
|
||||
@@ -83,6 +94,12 @@ if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => app.init());
|
||||
} else {
|
||||
app.init();
|
||||
window.addEventListener('beforeunload', () => {
|
||||
// 销毁定时器
|
||||
if (app.uiController && app.uiController.wsTimer) {
|
||||
clearInterval(app.uiController.wsTimer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default app;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// 背景图加载检测
|
||||
const backgroundContainer = document.getElementById('backgroundContainer');
|
||||
// 提取背景图片URL
|
||||
let bgImageUrl = window.getComputedStyle(backgroundContainer).backgroundImage;
|
||||
const urlMatch = bgImageUrl.match(/url\(["']?(.*?)["']?\)/);
|
||||
bgImageUrl = urlMatch[1];
|
||||
const bgImage = new Image();
|
||||
bgImage.src = bgImageUrl;
|
||||
// 加载完成后显示模型加载
|
||||
bgImage.onload = function () {
|
||||
const modelLoading = document.getElementById('modelLoading');
|
||||
if (modelLoading) {
|
||||
modelLoading.style.display = 'flex';
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ class UIController {
|
||||
this.visualizerCanvas = null;
|
||||
this.visualizerContext = null;
|
||||
this.audioStatsTimer = null;
|
||||
this.wsTimer = null;
|
||||
this.currentBackgroundIndex = 0;
|
||||
this.backgroundImages = ['1.png', '2.png', '3.png'];
|
||||
|
||||
@@ -275,7 +276,7 @@ class UIController {
|
||||
this.currentBackgroundIndex = (this.currentBackgroundIndex + 1) % this.backgroundImages.length;
|
||||
const backgroundContainer = document.querySelector('.background-container');
|
||||
if (backgroundContainer) {
|
||||
backgroundContainer.style.backgroundImage = `url('../images/${this.backgroundImages[this.currentBackgroundIndex]}')`;
|
||||
backgroundContainer.style.backgroundImage = `url('./images/${this.backgroundImages[this.currentBackgroundIndex]}')`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,6 +332,20 @@ class UIController {
|
||||
this.addChatMessage('配置已保存', false);
|
||||
}
|
||||
|
||||
// 拨号成功后直接开始录音
|
||||
dialAndRecord() {
|
||||
const recordBtn = document.getElementById('recordBtn');
|
||||
const wsHandler = getWebSocketHandler();
|
||||
this.wsTimer = setInterval(() => {
|
||||
if (wsHandler.isConnected() && wsHandler.websocket.onopen) {
|
||||
clearInterval(this.wsTimer);
|
||||
this.wsTimer = null;
|
||||
recordBtn.click();
|
||||
return;
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// 处理连接按钮点击
|
||||
async handleConnect() {
|
||||
console.log('handleConnect called');
|
||||
@@ -415,6 +430,8 @@ class UIController {
|
||||
dialBtn.disabled = false;
|
||||
dialBtn.querySelector('.btn-text').textContent = '挂断';
|
||||
dialBtn.classList.add('dial-active');
|
||||
|
||||
this.dialAndRecord();
|
||||
}
|
||||
|
||||
this.hideModal('settingsModal');
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
<body>
|
||||
<!-- 背景容器 -->
|
||||
<div class="background-container">
|
||||
<div class="background-container" id="backgroundContainer">
|
||||
<div class="background-overlay"></div>
|
||||
</div>
|
||||
|
||||
@@ -52,6 +52,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模型加载提示 -->
|
||||
<div class="model-container">
|
||||
<div class="model-loading" id="modelLoading">
|
||||
<div class="loading-char-float">
|
||||
<div class="main-char">
|
||||
<span>模</span><span>型</span><span>加</span><span>载</span><span>中</span><span>✨</span>
|
||||
</div>
|
||||
<div class="shadow-char">模型加载中✨</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Live2D Canvas 容器 -->
|
||||
<canvas id="live2d-stage"></canvas>
|
||||
|
||||
@@ -219,6 +231,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 背景图加载检测 -->
|
||||
<script src="js/ui/bg-image-load-detector.js"></script>
|
||||
|
||||
<!-- PIXI.js 2D渲染引擎 -->
|
||||
<script src="js/live2d/pixi.js"></script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user