feat: интеграция Lottie анимации AI robot в assist.html

This commit is contained in:
2026-05-25 18:01:35 +03:00
parent 26d09501fb
commit a31fb3a112
+66 -101
View File
@@ -4,6 +4,8 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>NOVA Assistant</title> <title>NOVA Assistant</title>
<!-- Lottie Player -->
<script src="https://unpkg.com/@dotlottie/player-component@latest/dist/dotlottie-player.mjs" type="module"></script>
<style> <style>
* { * {
margin: 0; margin: 0;
@@ -102,91 +104,51 @@
50% { transform: scale(1.2); opacity: 0.8; } 50% { transform: scale(1.2); opacity: 0.8; }
} }
/* Главная сфера (ORB) */ /* Главная сфера (ORB) - Lottie анимация */
.orb-container { .orb-container {
position: relative; position: relative;
width: 300px; width: 400px;
height: 300px; height: 400px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.orb { .lottie-player {
width: 250px; width: 100%;
height: 250px; height: 100%;
border-radius: 50%; filter: drop-shadow(0 0 40px rgba(0, 255, 255, 0.3));
background: radial-gradient(circle at 35% 35%, #00ffff, #0066aa);
box-shadow:
0 0 60px rgba(0, 255, 255, 0.4),
inset 0 0 40px rgba(255, 255, 255, 0.1);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
} }
/* Анимация пульсации при прослушивании */ /* Эффекты для разных состояний */
.orb.listening { .lottie-player.listening {
animation: orb-pulse 1.5s ease-in-out infinite; filter: drop-shadow(0 0 80px rgba(0, 255, 255, 0.8))
box-shadow: drop-shadow(0 0 120px rgba(0, 255, 255, 0.5));
0 0 100px rgba(0, 255, 255, 0.8), animation: lottie-pulse 1.5s ease-in-out infinite;
0 0 150px rgba(0, 255, 255, 0.4),
inset 0 0 60px rgba(255, 255, 255, 0.2);
} }
@keyframes orb-pulse { @keyframes lottie-pulse {
0%, 100% { 0%, 100% {
transform: scale(1); transform: scale(1);
box-shadow:
0 0 60px rgba(0, 255, 255, 0.4),
inset 0 0 40px rgba(255, 255, 255, 0.1);
} }
50% { 50% {
transform: scale(1.05); transform: scale(1.05);
box-shadow:
0 0 120px rgba(0, 255, 255, 0.8),
0 0 180px rgba(0, 255, 255, 0.5),
inset 0 0 80px rgba(255, 255, 255, 0.3);
} }
} }
/* Анимация при говорении */ .lottie-player.speaking {
.orb.speaking { filter: drop-shadow(0 0 80px rgba(255, 0, 255, 0.8))
animation: orb-speak 0.8s ease-in-out infinite; drop-shadow(0 0 120px rgba(255, 0, 255, 0.5));
background: radial-gradient(circle at 35% 35%, #ff00ff, #ff0088); animation: lottie-speak 0.8s ease-in-out infinite;
box-shadow:
0 0 100px rgba(255, 0, 255, 0.6),
0 0 150px rgba(255, 0, 255, 0.3);
} }
@keyframes orb-speak { @keyframes lottie-speak {
0%, 100% { transform: scale(1); } 0%, 100% { transform: scale(1); }
25% { transform: scale(1.03); } 25% { transform: scale(1.03); }
75% { transform: scale(0.98); } 75% { transform: scale(0.98); }
} }
/* RMS визуализация внутри сферы */
.orb-waves {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.wave-bar {
width: 6px;
height: 20px;
background: rgba(255, 255, 255, 0.6);
border-radius: 3px;
transition: height 0.1s ease;
}
/* Статус текст */ /* Статус текст */
.status-text { .status-text {
font-size: 24px; font-size: 24px;
@@ -383,13 +345,17 @@
<!-- Главный контейнер --> <!-- Главный контейнер -->
<div class="assistant-container"> <div class="assistant-container">
<!-- Сфера с анимацией --> <!-- Lottie анимация робота -->
<div class="orb-container"> <div class="orb-container">
<div class="orb" id="orb"> <dotlottie-player
<div class="orb-waves" id="orbWaves"> id="lottiePlayer"
<!-- Волны будут добавлены через JS --> class="lottie-player"
</div> src="AI robot.lottie"
</div> background="transparent"
speed="1"
loop
autoplay>
</dotlottie-player>
</div> </div>
<!-- Статус --> <!-- Статус -->
@@ -433,8 +399,7 @@
// ==================== DOM ЭЛЕМЕНТЫ ==================== // ==================== DOM ЭЛЕМЕНТЫ ====================
const statusDot = document.getElementById('statusDot'); const statusDot = document.getElementById('statusDot');
const statusLabel = document.getElementById('statusLabel'); const statusLabel = document.getElementById('statusLabel');
const orb = document.getElementById('orb'); const lottiePlayer = document.getElementById('lottiePlayer');
const orbWaves = document.getElementById('orbWaves');
const statusText = document.getElementById('statusText'); const statusText = document.getElementById('statusText');
const volumeSlider = document.getElementById('volumeSlider'); const volumeSlider = document.getElementById('volumeSlider');
const volumeValue = document.getElementById('volumeValue'); const volumeValue = document.getElementById('volumeValue');
@@ -442,19 +407,6 @@
const volDownBtn = document.getElementById('volDownBtn'); const volDownBtn = document.getElementById('volDownBtn');
const transcriptText = document.getElementById('transcriptText'); const transcriptText = document.getElementById('transcriptText');
// ==================== ИНИЦИАЛИЗАЦИЯ ====================
// Создать волновые бары
function initWaveBars() {
orbWaves.innerHTML = '';
for (let i = 0; i < NUM_WAVE_BARS; i++) {
const bar = document.createElement('div');
bar.className = 'wave-bar';
bar.dataset.index = i;
orbWaves.appendChild(bar);
}
}
// Создать фоновые частицы // Создать фоновые частицы
function initParticles() { function initParticles() {
const particles = document.getElementById('particles'); const particles = document.getElementById('particles');
@@ -468,28 +420,30 @@
} }
} }
// ==================== АНИМАЦИЯ ВОЛН ==================== // ==================== УПРАВЛЕНИЕ LOTTIE ====================
function animateWaves() { function updateLottieSpeed() {
const bars = orbWaves.querySelectorAll('.wave-bar'); if (!lottiePlayer) return;
bars.forEach((bar, index) => { // Изменяем скорость анимации в зависимости от состояния и RMS
let height = 20; let speed = 1.0;
if (currentState === 'listening' || currentState === 'speaking') { if (currentState === 'listening') {
// Анимация на основе RMS // При прослушивании скорость зависит от RMS
const rmsScaled = Math.min(1, currentRms * 5); // Усиление RMS const rmsScaled = Math.min(1, currentRms * 5);
const baseHeight = 20 + rmsScaled * 80; speed = 0.8 + rmsScaled * 1.2; // 0.8 - 2.0
const wave = Math.sin(Date.now() / 200 + index * 0.5) * 20; } else if (currentState === 'speaking') {
height = baseHeight + wave; speed = 1.5; // Быстрее при говорении
} else { } else {
// Минимальная анимация в idle speed = 0.6; // Медленнее в idle
height = 20 + Math.sin(Date.now() / 500 + index * 0.3) * 5;
} }
bar.style.height = height + 'px'; lottiePlayer.setSpeed(speed);
}); }
requestAnimationFrame(animateWaves); // Обновление скорости анимации в реальном времени
function animateLottie() {
updateLottieSpeed();
requestAnimationFrame(animateLottie);
} }
// ==================== WEBSOCKET ==================== // ==================== WEBSOCKET ====================
@@ -615,15 +569,19 @@
} }
} }
// ==================== ОБНОВЛЕНИЕ СОСТОЯНИЯ СФЕРЫ ==================== // ==================== ОБНОВЛЕНИЕ СОСТОЯНИЯ АНИМАЦИИ ====================
function updateOrbState() { function updateOrbState() {
orb.classList.remove('listening', 'speaking'); if (!lottiePlayer) return;
lottiePlayer.classList.remove('listening', 'speaking');
if (currentState === 'listening') { if (currentState === 'listening') {
orb.classList.add('listening'); lottiePlayer.classList.add('listening');
} else if (currentState === 'speaking') { } else if (currentState === 'speaking') {
orb.classList.add('speaking'); lottiePlayer.classList.add('speaking');
} }
updateLottieSpeed();
} }
// ==================== УПРАВЛЕНИЕ ГРОМКОСТЬЮ ==================== // ==================== УПРАВЛЕНИЕ ГРОМКОСТЬЮ ====================
@@ -664,10 +622,17 @@
// ==================== ЗАПУСК ==================== // ==================== ЗАПУСК ====================
window.addEventListener('load', () => { window.addEventListener('load', () => {
console.log('🚀 NOVA Assistant initialized'); console.log('🚀 NOVA Assistant initialized');
initWaveBars();
initParticles(); initParticles();
animateWaves(); animateLottie();
connectWebSocket(); connectWebSocket();
// Дождаться загрузки Lottie
if (lottiePlayer) {
lottiePlayer.addEventListener('ready', () => {
console.log('✅ Lottie animation loaded');
updateLottieSpeed();
});
}
}); });
</script> </script>