📱 ИНТЕРАКТИВНЫЙ ВЕБ-ИНТЕРФЕЙС УПРАВЛЕНИЯ
От статической панели к живому цифровому опыту
🎯 МЕТОДОЛОГИЧЕСКАЯ КОНЦЕПЦИЯ СПРИНТА
Философия трансформации:
БЫЛО: Веб-страница отвечает на клики (реактивный интерфейс)
СТАЛО: Интерфейс ведет диалог с пользователем (проактивный партнер)
Ключевая идея: Дети создают “цифрового помощника” - интерфейс, который не просто выполняет команды, а понимает контекст, предлагает решения и адаптируется под каждого пользователя.
Эволюционный скачок:
- Кнопки → Жесты: От механических кликов к естественным движениям
- Статика → Анимация: Каждое взаимодействие живое и отзывчивое
- Универсальность → Персонализация: Интерфейс помнит и адаптируется
- Инструмент → Собеседник: Система ведет диалог с пользователем
🧠 ПЕДАГОГИЧЕСКИЕ ЦЕЛИ СПРИНТА
Концептуальные цели:
- “Интерактивность” как двусторонний разговор человека и машины
- “Контекстное мышление” - система понимает ситуацию пользователя
- “Адаптивность” - интерфейс эволюционирует от использования
- “Эмпатический дизайн” - технология должна понимать эмоции
Технические цели:
- Advanced JavaScript: жесты, анимации, real-time обновления
- CSS3: transitions, transforms, keyframes для живого интерфейса
- Web APIs: геолокация, вибрация, battery, device orientation
- Progressive Web App (PWA) - превращаем в мобильное приложение
- Machine Learning на клиенте: TensorFlow.js для предсказаний
Метакогнитивные цели:
- “UX мышление” - как пользователь думает и чувствует
- “Поведенческий дизайн” - как интерфейс влияет на действия
- “Инклюзивное мышление” - дизайн для всех типов пользователей
📚 СТРУКТУРА СПРИНТА (4 занятия)
Занятие 1: “Психология взаимодействия” 🧠
Длительность: 90 минут
Фаза 1: Эксперимент “Эмоции интерфейса” (25 мин)
Метод: Экспериментальная психология UX
Практический эксперимент: Дети тестируют 3 версии одного интерфейса:
<!-- Версия 1: "Холодный робот" -->
<button onclick="turnOnLight()">ВКЛЮЧИТЬ СВЕТ</button>
<!-- Версия 2: "Дружелюбный помощник" -->
<button onclick="turnOnLight()" class="friendly">
☀️ Сделать светлее
</button>
<!-- Версия 3: "Умный собеседник" -->
<button onclick="smartLighting()" class="smart">
💡 <span id="lightSuggestion">Создать комфортное освещение</span>
</button>
Ключевые открытия:
- “Слова = эмоции. Технология тоже может быть дружелюбной”
- “Эмодзи = универсальный язык эмоций”
- “Предложения лучше команд”
Фаза 2: Анализ пользовательских ролей (30 мин)
Метод: Persona Development
Создание “Персон” для системы:
const userPersonas = {
teacher: {
name: "Мария Ивановна",
age: 45,
techLevel: "средний",
goals: ["быстро подготовить класс", "сэкономить время"],
frustrations: ["сложные интерфейсы", "медленные системы"],
preferredInteraction: "голосовые команды",
timeOfDay: "утро",
interface: {
buttonSize: "large",
textSize: "18px",
animations: "minimal",
shortcuts: true
}
},
student: {
name: "Максим",
age: 12,
techLevel: "высокий",
goals: ["изучить как работает", "поиграть с системой"],
frustrations: ["скучные интерфейсы", "много текста"],
preferredInteraction: "жесты и анимации",
timeOfDay: "любое",
interface: {
buttonSize: "medium",
textSize: "16px",
animations: "rich",
gamification: true
}
},
janitor: {
name: "Сергей Петрович",
age: 55,
techLevel: "низкий",
goals: ["проверить что все выключено", "безопасность"],
frustrations: ["сложная технология", "мелкий текст"],
preferredInteraction: "простые кнопки",
timeOfDay: "вечер",
interface: {
buttonSize: "extra-large",
textSize: "24px",
animations: "none",
highContrast: true
}
}
};
Практическое задание: Дети адаптируют один интерфейс под каждую персону.
Фаза 3: Принципы живого интерфейса (20 мин)
Концепция: “12 принципов интерактивности”
const interactivityPrinciples = {
1: "Немедленная реакция - система отвечает за 100мс",
2: "Визуальная обратная связь - пользователь видит результат",
3: "Тактильная обратная связь - вибрация подтверждает действие",
4: "Звуковая обратная связь - звуки создают атмосферу",
5: "Предсказание желаний - система предлагает действия",
6: "Контекстная адаптация - интерфейс меняется по ситуации",
7: "Прощение ошибок - любое действие можно отменить",
8: "Прогрессивное раскрытие - сложность появляется постепенно",
9: "Эмоциональный дизайн - интерфейс вызывает положительные эмоции",
10: "Инклюзивность - доступно для людей с ограничениями",
11: "Персонализация - система помнит предпочтения",
12: "Обучение - интерфейс становится умнее от использования"
};
Фаза 4: Wireframing интерактивного интерфейса (15 мин)
Метод: Collaborative Design
Дети рисуют схемы интерфейса с учетом всех принципов:
- Где располагаются элементы для каждой персоны
- Какие анимации и переходы будут
- Как система будет адаптироваться
- Какие жесты и взаимодействия поддерживать
Занятие 2: “Создание живого интерфейса” ✨
Длительность: 90 минут
Фаза 1: Продвинутый HTML с семантикой (20 мин)
Концепция: “HTML как язык смысла”
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="ALEX - Умная система управления классом">
<meta name="theme-color" content="#667eea">
<!-- PWA мета-теги -->
<link rel="manifest" href="/manifest.json">
<link rel="apple-touch-icon" href="/icon-192.png">
<title>🤖 ALEX - Ваш умный помощник</title>
<style>
/* CSS Custom Properties для динамической темизации */
:root {
--primary-color: #667eea;
--secondary-color: #764ba2;
--accent-color: #4ECDC4;
--text-color: white;
--background-opacity: 0.1;
--animation-speed: 0.3s;
--border-radius: 15px;
--shadow-color: rgba(0,0,0,0.2);
/* Адаптивные размеры */
--button-size: clamp(120px, 15vw, 200px);
--text-size: clamp(14px, 2vw, 18px);
--icon-size: clamp(2rem, 4vw, 4rem);
}
/* Темы для разных персон */
[data-persona="teacher"] {
--button-size: clamp(150px, 20vw, 250px);
--text-size: clamp(16px, 2.5vw, 20px);
--animation-speed: 0.2s;
}
[data-persona="student"] {
--accent-color: #FF6B6B;
--animation-speed: 0.4s;
--border-radius: 20px;
}
[data-persona="janitor"] {
--button-size: clamp(180px, 25vw, 300px);
--text-size: clamp(18px, 3vw, 24px);
--animation-speed: 0.1s;
--background-opacity: 0.2;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
min-height: 100vh;
overflow-x: hidden;
transition: all var(--animation-speed) ease;
user-select: none;
}
/* Адаптивный контейнер */
.container {
max-width: 1200px;
margin: 0 auto;
padding: clamp(15px, 3vw, 30px);
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Семантическая шапка */
.system-header {
text-align: center;
margin-bottom: 2rem;
animation: slideInDown 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.system-title {
font-size: clamp(2rem, 6vw, 4rem);
color: var(--text-color);
margin-bottom: 1rem;
text-shadow: 2px 2px 4px var(--shadow-color);
position: relative;
}
.system-title::after {
content: '';
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
width: 100px;
height: 3px;
background: var(--accent-color);
border-radius: 2px;
animation: expandWidth 1s ease-out 0.5s backwards;
}
@keyframes expandWidth {
from { width: 0; }
to { width: 100px; }
}
/* Адаптивная статус-панель */
.status-panel {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
background: rgba(255,255,255, var(--background-opacity));
padding: 1rem 2rem;
border-radius: var(--border-radius);
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.2);
margin-bottom: 2rem;
transition: all var(--animation-speed) ease;
}
.status-panel:hover {
transform: translateY(-2px);
box-shadow: 0 5px 25px var(--shadow-color);
}
.status-indicator {
display: flex;
align-items: center;
gap: 0.5rem;
}
.status-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--accent-color);
animation: pulse 2s infinite;
position: relative;
}
.status-dot::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
background: var(--accent-color);
animation: ripple 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
@keyframes ripple {
0% {
transform: scale(1);
opacity: 0.7;
}
100% {
transform: scale(2);
opacity: 0;
}
}
/* Основная область контента */
.main-content {
flex: 1;
display: grid;
gap: 2rem;
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
.main-content {
grid-template-columns: 2fr 1fr;
}
}
/* Интерактивная панель датчиков */
.sensors-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
}
.sensor-card {
background: rgba(255,255,255, var(--background-opacity));
backdrop-filter: blur(15px);
border-radius: var(--border-radius);
padding: 1.5rem;
border: 1px solid rgba(255,255,255,0.2);
position: relative;
overflow: hidden;
cursor: pointer;
transition: all var(--animation-speed) cubic-bezier(0.175, 0.885, 0.32, 1.275);
/* Подготовка к анимациям */
transform-origin: center;
will-change: transform;
}
.sensor-card::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
transition: left 0.5s ease;
z-index: 1;
}
.sensor-card:hover::before {
left: 100%;
}
.sensor-card:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 20px 40px var(--shadow-color);
border-color: rgba(255,255,255,0.4);
}
.sensor-card:active {
transform: translateY(-4px) scale(0.98);
}
/* Семантическая структура датчика */
.sensor-icon {
font-size: var(--icon-size);
margin-bottom: 1rem;
display: block;
transition: transform var(--animation-speed) ease;
position: relative;
z-index: 2;
}
.sensor-card:hover .sensor-icon {
transform: scale(1.2) rotate(5deg);
}
.sensor-name {
font-size: calc(var(--text-size) * 1.1);
color: rgba(255,255,255,0.9);
margin-bottom: 0.5rem;
font-weight: 600;
position: relative;
z-index: 2;
}
.sensor-value {
font-size: calc(var(--text-size) * 1.8);
font-weight: bold;
color: var(--text-color);
margin: 1rem 0;
position: relative;
z-index: 2;
transition: all var(--animation-speed) ease;
}
.sensor-status {
font-size: calc(var(--text-size) * 0.9);
color: rgba(255,255,255,0.8);
font-style: italic;
position: relative;
z-index: 2;
}
/* Визуальный прогресс-бар */
.sensor-progress {
width: 100%;
height: 6px;
background: rgba(255,255,255,0.2);
border-radius: 3px;
overflow: hidden;
margin-top: 1rem;
position: relative;
z-index: 2;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent-color), var(--primary-color));
border-radius: 3px;
transition: width 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
position: relative;
}
.progress-fill::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
animation: progressShine 2s infinite;
}
@keyframes progressShine {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
/* Панель управления */
.controls-section {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.section-title {
color: var(--text-color);
font-size: calc(var(--text-size) * 1.3);
margin-bottom: 1rem;
text-align: center;
position: relative;
}
.section-title::after {
content: '';
position: absolute;
bottom: -5px;
left: 50%;
transform: translateX(-50%);
width: 50px;
height: 2px;
background: var(--accent-color);
border-radius: 1px;
}
/* Умные кнопки управления */
.control-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(var(--button-size), 1fr));
gap: 1rem;
}
.smart-button {
background: rgba(255,255,255, var(--background-opacity));
border: 2px solid rgba(255,255,255,0.3);
border-radius: var(--border-radius);
padding: 1.5rem 1rem;
color: var(--text-color);
font-size: var(--text-size);
font-weight: 600;
cursor: pointer;
position: relative;
overflow: hidden;
transition: all var(--animation-speed) cubic-bezier(0.175, 0.885, 0.32, 1.275);
text-align: center;
user-select: none;
backdrop-filter: blur(10px);
/* Подготовка к трансформациям */
transform-origin: center;
will-change: transform;
}
.smart-button::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(circle at center, rgba(255,255,255,0.3) 0%, transparent 70%);
opacity: 0;
transition: opacity var(--animation-speed) ease;
z-index: 1;
}
.smart-button:hover::before {
opacity: 1;
}
.smart-button:hover {
transform: translateY(-4px) scale(1.05);
box-shadow: 0 15px 35px var(--shadow-color);
border-color: rgba(255,255,255,0.5);
background: rgba(255,255,255, calc(var(--background-opacity) + 0.1));
}
.smart-button:active {
transform: translateY(-2px) scale(0.95);
transition: transform 0.1s ease;
}
/* Содержимое кнопки */
.button-content {
position: relative;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.button-icon {
font-size: calc(var(--text-size) * 1.5);
margin-bottom: 0.5rem;
}
.button-text {
font-weight: 600;
line-height: 1.2;
}
.button-description {
font-size: calc(var(--text-size) * 0.8);
opacity: 0.8;
line-height: 1.2;
}
/* Специальные кнопки */
.smart-button.primary {
background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
border: none;
animation: primaryGlow 3s infinite;
}
@keyframes primaryGlow {
0%, 100% {
box-shadow: 0 5px 20px rgba(78, 205, 196, 0.4);
}
50% {
box-shadow: 0 5px 30px rgba(78, 205, 196, 0.6);
}
}
.smart-button.danger {
border-color: #ff6b6b;
color: #ff6b6b;
}
.smart-button.danger:hover {
background: rgba(255, 107, 107, 0.1);
border-color: #ff5252;
}
/* Рипл-эффект для кнопок */
.smart-button::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255,255,255,0.4);
transform: translate(-50%, -50%);
transition: width 0.6s ease, height 0.6s ease;
z-index: 1;
}
.smart-button:active::after {
width: 300px;
height: 300px;
}
/* Адаптивность */
@media (max-width: 768px) {
.main-content {
grid-template-columns: 1fr;
}
.sensors-section {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.sensor-card {
padding: 1rem;
}
.smart-button {
padding: 1rem 0.5rem;
}
}
/* Темы времени суток */
.theme-morning {
--primary-color: #ffeaa7;
--secondary-color: #fdcb6e;
--accent-color: #e17055;
}
.theme-evening {
--primary-color: #6c5ce7;
--secondary-color: #a29bfe;
--accent-color: #fd79a8;
}
.theme-night {
--primary-color: #2d3436;
--secondary-color: #636e72;
--accent-color: #00b894;
}
/* Анимации появления */
@keyframes slideInDown {
from {
transform: translateY(-50px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes slideInUp {
from {
transform: translateY(50px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes fadeInScale {
from {
transform: scale(0.8);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* Применение анимаций */
.sensor-card {
animation: fadeInScale 0.6s ease-out backwards;
}
.sensor-card:nth-child(1) { animation-delay: 0.1s; }
.sensor-card:nth-child(2) { animation-delay: 0.2s; }
.sensor-card:nth-child(3) { animation-delay: 0.3s; }
.sensor-card:nth-child(4) { animation-delay: 0.4s; }
.smart-button {
animation: slideInUp 0.6s ease-out backwards;
}
.smart-button:nth-child(1) { animation-delay: 0.5s; }
.smart-button:nth-child(2) { animation-delay: 0.6s; }
.smart-button:nth-child(3) { animation-delay: 0.7s; }
.smart-button:nth-child(4) { animation-delay: 0.8s; }
.smart-button:nth-child(5) { animation-delay: 0.9s; }
.smart-button:nth-child(6) { animation-delay: 1.0s; }
</style>
</head>
<body data-persona="student" id="app">
<div class="container">
<!-- Семантическая шапка -->
<header class="system-header">
<h1 class="system-title">🤖 ALEX</h1>
<div class="status-panel">
<div class="status-indicator">
<div class="status-dot" id="statusDot"></div>
<span id="systemStatus">Подключаюсь к системе...</span>
</div>
<div class="status-indicator">
<span id="connectionQuality">📶</span>
<span id="batteryLevel">🔋</span>
</div>
</div>
</header>
<!-- Основной контент -->
<main class="main-content">
<!-- Секция датчиков -->
<section class="sensors-section">
<article class="sensor-card" data-sensor="temperature" role="button" tabindex="0" aria-label="Датчик температуры">
<span class="sensor-icon" aria-hidden="true">🌡️</span>
<h3 class="sensor-name">Температура</h3>
<div class="sensor-value" id="temperature" aria-live="polite">--°C</div>
<p class="sensor-status" id="tempStatus">Загрузка...</p>
<div class="sensor-progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<div class="progress-fill" id="tempProgress" style="width: 0%"></div>
</div>
</article>
<article class="sensor-card" data-sensor="light" role="button" tabindex="0" aria-label="Датчик освещенности">
<span class="sensor-icon" aria-hidden="true">💡</span>
<h3 class="sensor-name">Освещенность</h3>
<div class="sensor-value" id="light" aria-live="polite">--%</div>
<p class="sensor-status" id="lightStatus">Загрузка...</p>
<div class="sensor-progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<div class="progress-fill" id="lightProgress" style="width: 0%"></div>
</div>
</article>
<article class="sensor-card" data-sensor="sound" role="button" tabindex="0" aria-label="Датчик звука">
<span class="sensor-icon" aria-hidden="true">🔊</span>
<h3 class="sensor-name">Уровень шума</h3>
<div class="sensor-value" id="sound" aria-live="polite">--дБ</div>
<p class="sensor-status" id="soundStatus">Загрузка...</p>
<div class="sensor-progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<div class="progress-fill" id="soundProgress" style="width: 0%"></div>
</div>
</article>
<article class="sensor-card" data-sensor="motion" role="button" tabindex="0" aria-label="Датчик движения">
<span class="sensor-icon" aria-hidden="true">🚶</span>
<h3 class="sensor-name">Движение</h3>
<div class="sensor-value" id="motion" aria-live="polite">--</div>
<p class="sensor-status" id="motionStatus">Загрузка...</p>
<div class="sensor-progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<div class="progress-fill" id="motionProgress" style="width: 0%"></div>
</div>
</article>
</section>
<!-- Панель управления -->
<aside class="controls-section">
<h2 class="section-title">🎛️ Умное управление</h2>
<div class="control-grid">
<button class="smart-button primary" data-mode="comfort" data-description="Идеальная температура и освещение">
<div class="button-content">
<span class="button-icon">😌</span>
<span class="button-text">Комфорт</span>
<span class="button-description">Создать уют</span>
</div>
</button>
<button class="smart-button" data-mode="eco" data-description="Экономия энергии с умом">
<div class="button-content">
<span class="button-icon">🌱</span>
<span class="button-text">Эко-режим</span>
<span class="button-description">Сберечь планету</span>
</div>
</button>
<button class="smart-button" data-mode="focus" data-description="Оптимальные условия для концентрации">
<div class="button-content">
<span class="button-icon">🎯</span>
<span class="button-text">Фокус</span>
<span class="button-description">Время учиться</span>
</div>
</button>
<button class="smart-button" data-mode="party" data-description="Атмосфера для веселья">
<div class="button-content">
<span class="button-icon">🎉</span>
<span class="button-text">Вечеринка</span>
<span class="button-description">Время радости</span>
</div>
</button>
<button class="smart-button" data-mode="presentation" data-description="Подготовка к показу">
<div class="button-content">
<span class="button-icon">📽️</span>
<span class="button-text">Презентация</span>
<span class="button-description">Все внимание</span>
</div>
</button>
<button class="smart-button danger" data-mode="emergency" data-description="Экстренное отключение">
<div class="button-content">
<span class="button-icon">🚨</span>
<span class="button-text">Экстренный</span>
<span class="button-description">Все выключить</span>
</div>
</button>
</div>
</aside>
</main>
</div>
</body>
</html>
Фаза 2: Продвинутый JavaScript для интерактивности (35 мин)
Концепция: “JavaScript как мозг интерфейса”
// === КЛАСС УМНОГО ИНТЕРФЕЙСА ===
class SmartInterface {
constructor() {
this.sensorData = {};
this.userPreferences = this.loadUserPreferences();
this.currentPersona = this.detectPersona();
this.socket = null;
this.isOnline = false;
this.gestureRecognizer = null;
this.init();
}
async init() {
console.log('🚀 Инициализация умного интерфейса...');
// Определяем возможности устройства
await this.detectDeviceCapabilities();
// Настраиваем персону
this.applyPersona(this.currentPersona);
// Подключаемся к системе
this.connectToSystem();
// Инициализируем взаимодействия
this.setupInteractions();
// Запускаем адаптивную логику
this.startAdaptiveEngine();
console.log('✅ Умный интерфейс готов!');
this.showWelcomeMessage();
}
// === ОПРЕДЕЛЕНИЕ ВОЗМОЖНОСТЕЙ УСТРОЙСТВА ===
async detectDeviceCapabilities() {
const capabilities = {
vibration: 'vibrate' in navigator,
geolocation: 'geolocation' in navigator,
battery: 'getBattery' in navigator,
deviceMotion: 'DeviceMotionEvent' in window,
touchGestures: 'ontouchstart' in window,
voiceRecognition: 'SpeechRecognition' in window || 'webkitSpeechRecognition' in window,
notifications: 'Notification' in window,
serviceWorker: 'serviceWorker' in navigator
};
this.deviceCapabilities = capabilities;
// Запрашиваем разрешения
if (capabilities.notifications && Notification.permission !== 'granted') {
await Notification.requestPermission();
}
// Получаем информацию о батарее
if (capabilities.battery) {
try {
this.battery = await navigator.getBattery();
this.updateBatteryIndicator();
this.battery.addEventListener('levelchange', () => this.updateBatteryIndicator());
} catch (e) {
console.log('Информация о батарее недоступна');
}
}
console.log('📱 Возможности устройства:', capabilities);
}
// === ОПРЕДЕЛЕНИЕ ПЕРСОНЫ ПОЛЬЗОВАТЕЛЯ ===
detectPersona() {
const hour = new Date().getHours();
const userAgent = navigator.userAgent;
const screenSize = window.innerWidth;
// Простая эвристика определения типа пользователя
if (hour >= 7 && hour <= 9) {
return 'teacher'; // Утром обычно учителя
} else if (hour >= 16 && hour <= 18) {
return 'janitor'; // Вечером техперсонал
} else if (screenSize < 768) {
return 'student'; // Мобильные устройства чаще у учеников
}
return 'student'; // По умолчанию
}
// === ПРИМЕНЕНИЕ ПЕРСОНЫ ===
applyPersona(persona) {
document.body.setAttribute('data-persona', persona);
const personaSettings = {
teacher: {
animationSpeed: '0.2s',
buttonSize: '200px',
textSize: '18px',
shortcuts: true,
voiceControl: true
},
student: {
animationSpeed: '0.4s',
buttonSize: '150px',
textSize: '16px',
richAnimations: true,
gameElements: true
},
janitor: {
animationSpeed: '0.1s',
buttonSize: '250px',
textSize: '20px',
highContrast: true,
largeTargets: true
}
};
const settings = personaSettings[persona];
if (settings) {
// Применяем CSS переменные для персоны
Object.entries(settings).forEach(([key, value]) => {
if (typeof value === 'string' && value.includes('px') || value.includes('s')) {
document.documentElement.style.setProperty(`--${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`, value);
}
});
// Специфичные настройки
if (settings.shortcuts) this.enableKeyboardShortcuts();
if (settings.voiceControl) this.enableVoiceControl();
if (settings.richAnimations) this.enableRichAnimations();
if (settings.gameElements) this.enableGameElements();
}
console.log(`👤 Применена персона: ${persona}`);
}
// === ПОДКЛЮЧЕНИЕ К СИСТЕМЕ ===
connectToSystem() {
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${wsProtocol}//${window.location.hostname}:81`;
try {
this.socket = new WebSocket(wsUrl);
this.socket.onopen = () => {
this.isOnline = true;
this.updateConnectionStatus('🟢 Подключен к системе', 'online');
console.log('🔗 WebSocket подключен');
// Отправляем информацию о пользователе
this.socket.send(JSON.stringify({
type: 'user_info',
persona: this.currentPersona,
capabilities: this.deviceCapabilities,
preferences: this.userPreferences
}));
};
this.socket.onmessage = (event) => {
this.handleSystemMessage(event.data);
};
this.socket.onclose = () => {
this.isOnline = false;
this.updateConnectionStatus('🔴 Переподключение...', 'reconnecting');
console.log('📡 WebSocket отключен, переподключаюсь...');
// Автоматическое переподключение
setTimeout(() => this.connectToSystem(), 3000);
};
this.socket.onerror = (error) => {
console.error('❌ Ошибка WebSocket:', error);
this.updateConnectionStatus('🔴 Ошибка соединения', 'error');
};
} catch (error) {
console.error('❌ Не удалось подключиться к системе:', error);
this.updateConnectionStatus('🔴 Офлайн режим', 'offline');
}
}
// === ОБРАБОТКА СООБЩЕНИЙ ОТ СИСТЕМЫ ===
handleSystemMessage(message) {
try {
const data = JSON.parse(message);
switch (data.type) {
case 'sensor_update':
this.updateSensorData(data);
break;
case 'system_alert':
this.showAlert(data.message, data.level);
break;
case 'suggestion':
this.showSuggestion(data.suggestion);
break;
case 'mood_update':
this.updateSystemMood(data.mood);
break;
default:
console.log('💬 Сообщение от системы:', message);
}
} catch (e) {
// Обычное текстовое сообщение
console.log('📝 Текст от системы:', message);
}
}
// === ОБНОВЛЕНИЕ ДАННЫХ ДАТЧИКОВ ===
updateSensorData(data) {
const oldData = { ...this.sensorData };
this.sensorData = data;
// Анимированное обновление каждого датчика
this.animateValueUpdate('temperature', data.temperature + '°C', oldData.temperature);
this.animateValueUpdate('light', Math.round((data.light / 4095) * 100) + '%', oldData.light);
this.animateValueUpdate('sound', Math.round((data.sound / 4095) * 100) + 'дБ', oldData.sound);
this.animateValueUpdate('motion', data.motion ? 'Есть' : 'Нет', oldData.motion);
// Обновляем прогресс-бары
this.updateProgressBar('tempProgress', data.temperature, 0, 40);
this.updateProgressBar('lightProgress', (data.light / 4095) * 100, 0, 100);
this.updateProgressBar('soundProgress', (data.sound / 4095) * 100, 0, 100);
this.updateProgressBar('motionProgress', data.motion ? 100 : 0, 0, 100);
// Обновляем статусы
this.updateSensorStatus('tempStatus', this.getTemperatureStatus(data.temperature));
this.updateSensorStatus('lightStatus', this.getLightStatus((data.light / 4095) * 100));
this.updateSensorStatus('soundStatus', this.getSoundStatus((data.sound / 4095) * 100));
this.updateSensorStatus('motionStatus', this.getMotionStatus(data.motion));
// Проверяем нужны ли предложения
this.checkForSuggestions(data);
// Адаптируем интерфейс под условия
this.adaptInterfaceToConditions(data);
}
// === АНИМИРОВАННОЕ ОБНОВЛЕНИЕ ЗНАЧЕНИЙ ===
animateValueUpdate(elementId, newValue, oldValue) {
const element = document.getElementById(elementId);
const currentValue = element.textContent;
if (currentValue !== newValue) {
// Создаем эффект изменения
element.style.transform = 'scale(1.1)';
element.style.color = '#FFD93D';
// Добавляем вибрацию на мобильных
if (this.deviceCapabilities.vibration && oldValue !== undefined) {
navigator.vibrate(50);
}
setTimeout(() => {
element.textContent = newValue;
element.style.transform = 'scale(1)';
element.style.color = '';
// Подсвечиваем родительскую карточку
const card = element.closest('.sensor-card');
if (card) {
card.style.boxShadow = '0 0 30px rgba(255, 217, 61, 0.5)';
setTimeout(() => {
card.style.boxShadow = '';
}, 800);
}
}, 200);
// Обновляем ARIA атрибуты для скринридеров
element.setAttribute('aria-live', 'polite');
}
}
// === ОБНОВЛЕНИЕ ПРОГРЕСС-БАРОВ ===
updateProgressBar(progressId, value, min, max) {
const progressElement = document.getElementById(progressId);
const percentage = Math.max(0, Math.min(100, ((value - min) / (max - min)) * 100));
// Плавная анимация изменения
progressElement.style.width = percentage + '%';
// Обновляем ARIA атрибуты
const progressBar = progressElement.closest('[role="progressbar"]');
if (progressBar) {
progressBar.setAttribute('aria-valuenow', Math.round(percentage));
}
}
// === УМНЫЕ СТАТУСЫ ДАТЧИКОВ ===
getTemperatureStatus(temp) {
if (temp < 18) return { message: '🥶 Холодно! Предлагаю включить обогрев', color: '#4FC3F7', action: 'heating' };
if (temp > 26) return { message: '🔥 Жарко! Нужно охлаждение', color: '#FF7043', action: 'cooling' };
if (temp >= 20 && temp <= 24) return { message: '😌 Идеальная температура', color: '#66BB6A', action: null };
return { message: '🌡️ Нормальная температура', color: '#FFD54F', action: null };
}
getLightStatus(light) {
if (light < 20) return { message: '🌙 Темно! Включить освещение?', color: '#9575CD', action: 'lighting' };
if (light > 80) return { message: '☀️ Очень светло', color: '#FFB74D', action: null };
return { message: '👍 Комфортное освещение', color: '#81C784', action: null };
}
getSoundStatus(sound) {
if (sound < 20) return { message: '🤫 Очень тихо', color: '#4DB6AC', action: null };
if (sound > 70) return { message: '📢 Шумно! Попросить потише?', color: '#E57373', action: 'noise_control' };
return { message: '🎵 Комфортный уровень звука', color: '#AED581', action: null };
}
getMotionStatus(motion) {
return motion ?
{ message: '👥 Люди в классе', color: '#64B5F6', action: null } :
{ message: '🏫 Класс пустой - эко-режим?', color: '#90A4AE', action: 'eco_mode' };
}
// === ОБНОВЛЕНИЕ СТАТУСОВ ===
updateSensorStatus(statusId, status) {
const statusElement = document.getElementById(statusId);
statusElement.textContent = status.message;
statusElement.style.color = status.color;
// Если есть предлагаемое действие, показываем кнопку
if (status.action) {
this.showActionSuggestion(statusId, status.action, status.message);
}
}
// === ПРЕДЛОЖЕНИЯ ДЕЙСТВИЙ ===
showActionSuggestion(statusId, action, message) {
const statusElement = document.getElementById(statusId);
const existingSuggestion = statusElement.parentElement.querySelector('.action-suggestion');
if (existingSuggestion) {
existingSuggestion.remove();
}
const suggestionButton = document.createElement('button');
suggestionButton.className = 'action-suggestion';
suggestionButton.innerHTML = '✨ Исправить';
suggestionButton.style.cssText = `
margin-top: 8px;
padding: 6px 12px;
background: rgba(255,255,255,0.2);
border: 1px solid rgba(255,255,255,0.3);
border-radius: 15px;
color: white;
font-size: 12px;
cursor: pointer;
transition: all 0.3s ease;
`;
suggestionButton.addEventListener('click', () => {
this.executeQuickAction(action);
suggestionButton.remove();
});
suggestionButton.addEventListener('mouseenter', () => {
suggestionButton.style.background = 'rgba(255,255,255,0.3)';
suggestionButton.style.transform = 'scale(1.05)';
});
suggestionButton.addEventListener('mouseleave', () => {
suggestionButton.style.background = 'rgba(255,255,255,0.2)';
suggestionButton.style.transform = 'scale(1)';
});
statusElement.parentElement.appendChild(suggestionButton);
}
// === БЫСТРЫЕ ДЕЙСТВИЯ ===
async executeQuickAction(action) {
const actionMap = {
'heating': () => this.executeSmartMode('comfort'),
'cooling': () => this.executeSmartMode('cool'),
'lighting': () => this.executeSmartMode('bright'),
'noise_control': () => this.showNoiseAlert(),
'eco_mode': () => this.executeSmartMode('eco')
};
if (actionMap[action]) {
await actionMap[action]();
}
}
// === НАСТРОЙКА ВЗАИМОДЕЙСТВИЙ ===
setupInteractions() {
// Обработчики кнопок управления
document.querySelectorAll('.smart-button').forEach(button => {
this.setupSmartButton(button);
});
// Обработчики карточек датчиков
document.querySelectorAll('.sensor-card').forEach(card => {
this.setupSensorCard(card);
});
// Клавиатурные сокращения
this.setupKeyboardShortcuts();
// Жесты
if (this.deviceCapabilities.touchGestures) {
this.setupTouchGestures();
}
// Голосовое управление
if (this.deviceCapabilities.voiceRecognition) {
this.setupVoiceControl();
}
// Адаптация к ориентации устройства
if (this.deviceCapabilities.deviceMotion) {
this.setupOrientationAdaptation();
}
}
// === НАСТРОЙКА УМНЫХ КНОПОК ===
setupSmartButton(button) {
const mode = button.dataset.mode;
button.addEventListener('click', async (e) => {
e.preventDefault();
await this.executeSmartMode(mode, button);
});
// Предварительный просмотр при наведении
button.addEventListener('mouseenter', () => {
this.showModePreview(mode, button);
});
button.addEventListener('mouseleave', () => {
this.hideModePreview();
});
// Поддержка клавиатуры
button.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
button.click();
}
});
}
// === ВЫПОЛНЕНИЕ УМНОГО РЕЖИМА ===
async executeSmartMode(mode, buttonElement = null) {
if (buttonElement) {
// Визуальная обратная связь
const originalContent = buttonElement.innerHTML;
buttonElement.innerHTML = '<div class="button-content"><span class="button-icon">⏳</span><span class="button-text">Применяю...</span></div>';
buttonElement.disabled = true;
// Тактильная обратная связь
if (this.deviceCapabilities.vibration) {
navigator.vibrate([100, 50, 100]);
}
}
try {
const response = await fetch(`/api/smart-mode/${mode}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
currentSensors: this.sensorData,
userPreferences: this.userPreferences,
persona: this.currentPersona,
deviceCapabilities: this.deviceCapabilities,
timestamp: Date.now()
})
});
const result = await response.json();
if (result.success) {
if (buttonElement) {
buttonElement.innerHTML = '<div class="button-content"><span class="button-icon">✅</span><span class="button-text">Готово!</span></div>';
buttonElement.style.background = 'linear-gradient(135deg, #4CAF50, #45a049)';
}
// Показываем детали изменений
this.showModeResult(mode, result);
// Обучаем систему на выборе пользователя
this.learnFromUserChoice(mode, this.sensorData);
// Показываем уведомление
this.showNotification(`Режим "${this.getModeTitle(mode)}" активирован`, 'success');
} else {
throw new Error(result.message || 'Ошибка выполнения');
}
} catch (error) {
console.error('❌ Ошибка выполнения режима:', error);
if (buttonElement) {
buttonElement.innerHTML = '<div class="button-content"><span class="button-icon">❌</span><span class="button-text">Ошибка</span></div>';
buttonElement.style.background = 'linear-gradient(135deg, #f44336, #d32f2f)';
}
this.showNotification('Ошибка выполнения команды', 'error');
} finally {
// Возвращаем кнопку в исходное состояние
if (buttonElement) {
setTimeout(() => {
buttonElement.innerHTML = buttonElement.dataset.originalContent || originalContent;
buttonElement.style.background = '';
buttonElement.disabled = false;
}, 2000);
}
}
}
// === ПРЕДВАРИТЕЛЬНЫЙ ПРОСМОТР РЕЖИМА ===
showModePreview(mode, button) {
const preview = this.getModePreview(mode);
// Создаем всплывающую подсказку
const tooltip = document.createElement('div');
tooltip.className = 'mode-preview-tooltip';
tooltip.innerHTML = `
<h4>${preview.title}</h4>
<p>${preview.description}</p>
<ul>
${preview.effects.map(effect => `<li>${effect}</li>`).join('')}
</ul>
`;
tooltip.style.cssText = `
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.9);
color: white;
padding: 15px;
border-radius: 10px;
font-size: 14px;
max-width: 250px;
z-index: 1000;
animation: fadeInUp 0.3s ease;
box-shadow: 0 5px 20px rgba(0,0,0,0.3);
`;
button.style.position = 'relative';
button.appendChild(tooltip);
}
hideModePreview() {
document.querySelectorAll('.mode-preview-tooltip').forEach(tooltip => {
tooltip.style.animation = 'fadeOut 0.2s ease';
setTimeout(() => tooltip.remove(), 200);
});
}
getModePreview(mode) {
const previews = {
comfort: {
title: '😌 Комфортный режим',
description: 'Создаю идеальные условия для отдыха и работы',
effects: ['🌡️ Температура 22°C', '💡 Мягкое освещение', '🔇 Контроль шума']
},
eco: {
title: '🌱 Эко-режим',
description: 'Экономлю энергию с умом',
effects: ['⚡ Снижение потребления на 40%', '🌍 Забота об экологии', '💚 Умная оптимизация']
},
focus: {
title: '🎯 Режим концентрации',
description: 'Оптимальные условия для учебы',
effects: ['🧠 Температура для мозга 21°C', '📚 Яркое освещение', '🤫 Минимум отвлечений']
},
party: {
title: '🎉 Вечеринка',
description: 'Создаю атмосферу веселья',
effects: ['🌟 Яркое освещение', '🎵 Разрешен шум', '💃 Энергичная атмосфера']
},
presentation: {
title: '📽️ Презентация',
description: 'Готовлю класс для показа',
effects: ['🔅 Приглушенный свет', '👥 Комфорт для аудитории', '📊 Фокус на экране']
}
};
return previews[mode] || { title: mode, description: 'Специальный режим', effects: [] };
}
// === НАСТРОЙКА КАРТОЧЕК ДАТЧИКОВ ===
setupSensorCard(card) {
const sensorType = card.dataset.sensor;
card.addEventListener('click', () => {
this.showSensorDetails(sensorType);
});
// Двойной клик для быстрой калибровки
card.addEventListener('dblclick', () => {
this.calibrateSensor(sensorType);
});
// Поддержка клавиатуры
card.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
this.showSensorDetails(sensorType);
}
});
}
showSensorDetails(sensorType) {
// Показываем детальную аналитику датчика
console.log(`📊 Детали датчика: ${sensorType}`);
const modal = this.createModal(`
<h3>📊 Аналитика датчика: ${this.getSensorName(sensorType)}</h3>
<div class="sensor-analytics">
<div class="chart-container">
<canvas id="sensorChart" width="300" height="200"></canvas>
</div>
<div class="stats">
<p><strong>Текущее значение:</strong> <span id="currentValue">--</span></p>
<p><strong>Среднее за день:</strong> <span id="avgValue">--</span></p>
<p><strong>Минимум:</strong> <span id="minValue">--</span></p>
<p><strong>Максимум:</strong> <span id="maxValue">--</span></p>
</div>
<button onclick="smartInterface.calibrateSensor('${sensorType}')" class="calibrate-btn">
🔧 Калибровать датчик
</button>
</div>
`);
// Здесь можно добавить реальную визуализацию данных
this.drawSensorChart(sensorType);
}
getSensorName(type) {
const names = {
temperature: '🌡️ Температура',
light: '💡 Освещенность',
sound: '🔊 Звук',
motion: '🚶 Движение'
};
return names[type] || type;
}
// === ГОЛОСОВОЕ УПРАВЛЕНИЕ ===
setupVoiceControl() {
if (!this.deviceCapabilities.voiceRecognition) return;
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
this.recognition = new SpeechRecognition();
this.recognition.lang = 'ru-RU';
this.recognition.continuous = false;
this.recognition.interimResults = false;
this.recognition.onresult = (event) => {
const command = event.results[0][0].transcript.toLowerCase();
this.processVoiceCommand(command);
};
// Кнопка активации голосового управления
const voiceButton = this.createVoiceButton();
document.body.appendChild(voiceButton);
}
processVoiceCommand(command) {
console.log(`🎤 Голосовая команда: ${command}`);
const commandMap = {
'комфорт': () => this.executeSmartMode('comfort'),
'эко режим': () => this.executeSmartMode('eco'),
'фокус': () => this.executeSmartMode('focus'),
'вечеринка': () => this.executeSmartMode('party'),
'презентация': () => this.executeSmartMode('presentation'),
'включить свет': () => this.executeQuickAction('lighting'),
'включить обогрев': () => this.executeQuickAction('heating'),
'статус системы': () => this.showSystemStatus()
};
for (const [pattern, action] of Object.entries(commandMap)) {
if (command.includes(pattern)) {
action();
this.speak(`Выполняю команду: ${pattern}`);
return;
}
}
this.speak('Команда не распознана. Повторите, пожалуйста.');
}
speak(text) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'ru-RU';
speechSynthesis.speak(utterance);
}
// === ЖЕСТОВОЕ УПРАВЛЕНИЕ ===
setupTouchGestures() {
let startX, startY, endX, endY;
document.addEventListener('touchstart', (e) => {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
});
document.addEventListener('touchend', (e) => {
endX = e.changedTouches[0].clientX;
endY = e.changedTouches[0].clientY;
this.handleGesture(startX, startY, endX, endY);
});
}
handleGesture(startX, startY, endX, endY) {
const deltaX = endX - startX;
const deltaY = endY - startY;
const threshold = 100;
if (Math.abs(deltaX) > threshold || Math.abs(deltaY) > threshold) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
// Горизонтальный свайп
if (deltaX > 0) {
this.executeSmartMode('comfort'); // Свайп вправо = комфорт
} else {
this.executeSmartMode('eco'); // Свайп влево = эко
}
} else {
// Вертикальный свайп
if (deltaY < 0) {
this.showSystemStatus(); // Свайп вверх = статус
} else {
this.hideAllModals(); // Свайп вниз = закрыть
}
}
}
}
// === АДАПТАЦИЯ К УСЛОВИЯМ ===
adaptInterfaceToConditions(data) {
// Меняем тему в зависимости от времени суток
const hour = new Date().getHours();
let theme = 'default';
if (hour >= 6 && hour < 12) {
theme = 'morning';
} else if (hour >= 12 && hour < 18) {
theme = 'day';
} else if (hour >= 18 && hour < 22) {
theme = 'evening';
} else {
theme = 'night';
}
document.body.className = `theme-${theme}`;
// Адаптируем яркость интерфейса к освещенности
const lightLevel = (data.light / 4095) * 100;
if (lightLevel < 30) {
document.documentElement.style.setProperty('--background-opacity', '0.05');
document.documentElement.style.setProperty('--text-color', '#ffffff');
} else {
document.documentElement.style.setProperty('--background-opacity', '0.1');
document.documentElement.style.setProperty('--text-color', '#ffffff');
}
}
// === УВЕДОМЛЕНИЯ И МОДАЛЬНЫЕ ОКНА ===
showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.innerHTML = `
<div class="notification-content">
<span class="notification-icon">${this.getNotificationIcon(type)}</span>
<span class="notification-text">${message}</span>
<button class="notification-close" onclick="this.parentElement.parentElement.remove()">×</button>
</div>
`;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${this.getNotificationColor(type)};
color: white;
padding: 15px 20px;
border-radius: 10px;
box-shadow: 0 5px 20px rgba(0,0,0,0.3);
z-index: 1000;
animation: slideInRight 0.5s ease;
max-width: 300px;
`;
document.body.appendChild(notification);
// Автоудаление через 5 секунд
setTimeout(() => {
if (notification.parentElement) {
notification.style.animation = 'slideOutRight 0.5s ease';
setTimeout(() => notification.remove(), 500);
}
}, 5000);
// Push-уведомление если разрешено
if (this.deviceCapabilities.notifications && Notification.permission === 'granted') {
new Notification('🤖 ALEX', {
body: message,
icon: '/icon-192.png'
});
}
}
getNotificationIcon(type) {
const icons = {
success: '✅',
error: '❌',
warning: '⚠️',
info: 'ℹ️'
};
return icons[type] || 'ℹ️';
}
getNotificationColor(type) {
const colors = {
success: 'linear-gradient(135deg, #4CAF50, #45a049)',
error: 'linear-gradient(135deg, #f44336, #d32f2f)',
warning: 'linear-gradient(135deg, #ff9800, #f57c00)',
info: 'linear-gradient(135deg, #2196F3, #1976D2)'
};
return colors[type] || colors.info;
}
createModal(content) {
const modal = document.createElement('div');
modal.className = 'modal-overlay';
modal.innerHTML = `
<div class="modal-content">
<button class="modal-close" onclick="this.closest('.modal-overlay').remove()">×</button>
${content}
</div>
`;
modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 2000;
animation: fadeIn 0.3s ease;
`;
document.body.appendChild(modal);
return modal;
}
// === СОХРАНЕНИЕ ПРЕДПОЧТЕНИЙ ===
saveUserPreferences() {
localStorage.setItem('alexUserPreferences', JSON.stringify(this.userPreferences));
}
loadUserPreferences() {
const stored = localStorage.getItem('alexUserPreferences');
return stored ? JSON.parse(stored) : {
preferredMode: 'comfort',
notifications: true,
voiceControl: false,
animations: true,
theme: 'auto'
};
}
learnFromUserChoice(mode, sensorData) {
// Обучение на основе выбора пользователя
this.userPreferences.preferredMode = mode;
this.userPreferences.lastUsed = Date.now();
// Запоминаем контекст выбора
if (!this.userPreferences.contextualChoices) {
this.userPreferences.contextualChoices = [];
}
this.userPreferences.contextualChoices.push({
mode: mode,
temperature: sensorData.temperature,
light: sensorData.light,
time: new Date().getHours(),
timestamp: Date.now()
});
// Ограничиваем историю последними 50 выборами
if (this.userPreferences.contextualChoices.length > 50) {
this.userPreferences.contextualChoices = this.userPreferences.contextualChoices.slice(-50);
}
this.saveUserPreferences();
console.log('🧠 Система изучила предпочтения пользователя');
}
// === ОБНОВЛЕНИЕ СТАТУСА ===
updateConnectionStatus(status, type) {
document.getElementById('systemStatus').textContent = status;
const statusDot = document.getElementById('statusDot');
const colors = {
online: '#4CAF50',
offline: '#f44336',
reconnecting: '#ff9800',
error: '#f44336'
};
statusDot.style.backgroundColor = colors[type] || colors.offline;
}
updateBatteryIndicator() {
if (!this.battery) return;
const batteryElement = document.getElementById('batteryLevel');
const level = Math.round(this.battery.level * 100);
let icon = '🔋';
if (level < 20) icon = '🪫';
else if (level < 50) icon = '🔋';
else icon = '🔋';
if (this.battery.charging) icon = '⚡';
batteryElement.textContent = icon;
batteryElement.title = `Батарея: ${level}%`;
}
showWelcomeMessage() {
const persona = this.currentPersona;
const messages = {
teacher: '👋 Добро пожаловать! Готов помочь с управлением классом.',
student: '🎉 Привет! Давай изучать умные технологии вместе!',
janitor: '🔧 Здравствуйте! Помогу следить за всеми системами.'
};
setTimeout(() => {
this.showNotification(messages[persona] || messages.student, 'info');
}, 1000);
}
}
// === ИНИЦИАЛИЗАЦИЯ ===
let smartInterface;
document.addEventListener('DOMContentLoaded', function() {
smartInterface = new SmartInterface();
});
// === ДОПОЛНИТЕЛЬНЫЕ CSS АНИМАЦИИ ===
const additionalStyles = `
@keyframes slideInRight {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes slideOutRight {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(100%); opacity: 0; }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.modal-content {
background: white;
padding: 30px;
border-radius: 15px;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
position: relative;
color: #333;
}
.modal-close {
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #999;
}
.notification-content {
display: flex;
align-items: center;
gap: 10px;
}
.notification-close {
background: none;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
margin-left: auto;
}
`;
// Добавляем дополнительные стили
const styleSheet = document.createElement('style');
styleSheet.textContent = additionalStyles;
document.head.appendChild(styleSheet);
Занятие 3: “PWA и адаптивность” 📱
Длительность: 90 минут
Создание Progressive Web App (PWA):
// manifest.json
{
"name": "ALEX - Умная система класса",
"short_name": "ALEX",
"description": "Интеллектуальная система управления классом",
"start_url": "/",
"display": "standalone",
"theme_color": "#667eea",
"background_color": "#667eea",
"orientation": "portrait-primary",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"categories": ["education", "productivity"],
"screenshots": [
{
"src": "/screenshot1.png",
"sizes": "1080x1920",
"type": "image/png"
}
]
}
Service Worker для офлайн работы:
// sw.js
const CACHE_NAME = 'alex-v1.0.0';
const urlsToCache = [
'/',
'/styles.css',
'/script.js',
'/manifest.json',
'/icon-192.png',
'/icon-512.png'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
return response || fetch(event.request);
}
)
);
});
Занятие 4: “Тестирование и оптимизация” 🧪
Длительность: 90 минут
A/B тестирование интерфейсов:
- Дети создают 2 версии одного элемента
- Тестируют на разных пользователях
- Анализируют какой вариант лучше
Оптимизация производительности:
- Минификация CSS/JS
- Ленивая загрузка изображений
- Кэширование данных
🎯 ИТОГИ СПРИНТА 18
Ключевые достижения:
✅ Интерактивный интерфейс - живое взаимодействие с пользователем
✅ Адаптивность - подстройка под разные типы пользователей
✅ Мультимодальность - голос, жесты, клавиатура, касания
✅ PWA - веб-приложение как нативное мобильное
✅ Персонализация - система запоминает предпочтения
✅ Доступность - интерфейс для всех категорий пользователей
Концептуальные прорывы:
- UX-мышление - понимание потребностей пользователя
- Эмпатический дизайн - технология с человеческим лицом
- Адаптивные системы - интерфейс как живой организм
- Мультимодальное взаимодействие - общение на языке пользователя
Технические навыки:
- Продвинутый JavaScript и CSS3
- Web APIs и PWA технологии
- Accessibility и инклюзивный дизайн
- UX/UI принципы и методологии
🚀 ПОДГОТОВКА К СПРИНТУ 19
Мостик к машинному обучению:
“Наш интерфейс умеет адаптироваться, но что если он научится предсказывать желания пользователей и самостоятельно предлагать оптимальные решения?”
Фундамент для AI:
- ✅ Сбор данных о поведении пользователей
- ✅ Контекстное понимание ситуаций
- ✅ Персонализация и обучение на предпочтениях
- ✅ Предиктивные предложения действий
Спринт 18 завершен! 📱
Дети создали по-настоящему живой интерфейс, который понимает пользователей и адаптируется под них!
Готов к анализу Спринта 19: “Введение в Machine Learning”! 🤖🧠
