Skip to main content

📱 ИНТЕРАКТИВНЫЙ ВЕБ-ИНТЕРФЕЙС УПРАВЛЕНИЯ

От статической панели к живому цифровому опыту


🎯 МЕТОДОЛОГИЧЕСКАЯ КОНЦЕПЦИЯ СПРИНТА

Философия трансформации:

1БЫЛО: Веб-страница отвечает на клики (реактивный интерфейс)
2СТАЛО: Интерфейс ведет диалог с пользователем (проактивный партнер)

Ключевая идея: Дети создают “цифрового помощника” - интерфейс, который не просто выполняет команды, а понимает контекст, предлагает решения и адаптируется под каждого пользователя.

Эволюционный скачок:

  • Кнопки → Жесты: От механических кликов к естественным движениям
  • Статика → Анимация: Каждое взаимодействие живое и отзывчивое
  • Универсальность → Персонализация: Интерфейс помнит и адаптируется
  • Инструмент → Собеседник: Система ведет диалог с пользователем

🧠 ПЕДАГОГИЧЕСКИЕ ЦЕЛИ СПРИНТА

Концептуальные цели:

  • “Интерактивность” как двусторонний разговор человека и машины
  • “Контекстное мышление” - система понимает ситуацию пользователя
  • “Адаптивность” - интерфейс эволюционирует от использования
  • “Эмпатический дизайн” - технология должна понимать эмоции

Технические цели:

  • 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<!-- Версия 1: "Холодный робот" -->
 2<button onclick="turnOnLight()">ВКЛЮЧИТЬ СВЕТ</button>
 3
 4<!-- Версия 2: "Дружелюбный помощник" -->
 5<button onclick="turnOnLight()" class="friendly">
 6    ☀️ Сделать светлее
 7</button>
 8
 9<!-- Версия 3: "Умный собеседник" -->
10<button onclick="smartLighting()" class="smart">
11    💡 <span id="lightSuggestion">Создать комфортное освещение</span>
12</button>

Ключевые открытия:

  • “Слова = эмоции. Технология тоже может быть дружелюбной”
  • “Эмодзи = универсальный язык эмоций”
  • “Предложения лучше команд”

Фаза 2: Анализ пользовательских ролей (30 мин)

Метод: Persona Development

Создание “Персон” для системы:

 1const userPersonas = {
 2    teacher: {
 3        name: "Мария Ивановна",
 4        age: 45,
 5        techLevel: "средний",
 6        goals: ["быстро подготовить класс", "сэкономить время"],
 7        frustrations: ["сложные интерфейсы", "медленные системы"],
 8        preferredInteraction: "голосовые команды",
 9        timeOfDay: "утро",
10        interface: {
11            buttonSize: "large",
12            textSize: "18px",
13            animations: "minimal",
14            shortcuts: true
15        }
16    },
17    
18    student: {
19        name: "Максим",
20        age: 12,
21        techLevel: "высокий",
22        goals: ["изучить как работает", "поиграть с системой"],
23        frustrations: ["скучные интерфейсы", "много текста"],
24        preferredInteraction: "жесты и анимации",
25        timeOfDay: "любое",
26        interface: {
27            buttonSize: "medium",
28            textSize: "16px", 
29            animations: "rich",
30            gamification: true
31        }
32    },
33    
34    janitor: {
35        name: "Сергей Петрович",
36        age: 55,
37        techLevel: "низкий",
38        goals: ["проверить что все выключено", "безопасность"],
39        frustrations: ["сложная технология", "мелкий текст"],
40        preferredInteraction: "простые кнопки",
41        timeOfDay: "вечер",
42        interface: {
43            buttonSize: "extra-large",
44            textSize: "24px",
45            animations: "none",
46            highContrast: true
47        }
48    }
49};

Практическое задание: Дети адаптируют один интерфейс под каждую персону.

Фаза 3: Принципы живого интерфейса (20 мин)

Концепция: “12 принципов интерактивности”

 1const interactivityPrinciples = {
 2    1: "Немедленная реакция - система отвечает за 100мс",
 3    2: "Визуальная обратная связь - пользователь видит результат",
 4    3: "Тактильная обратная связь - вибрация подтверждает действие", 
 5    4: "Звуковая обратная связь - звуки создают атмосферу",
 6    5: "Предсказание желаний - система предлагает действия",
 7    6: "Контекстная адаптация - интерфейс меняется по ситуации",
 8    7: "Прощение ошибок - любое действие можно отменить",
 9    8: "Прогрессивное раскрытие - сложность появляется постепенно",
10    9: "Эмоциональный дизайн - интерфейс вызывает положительные эмоции",
11    10: "Инклюзивность - доступно для людей с ограничениями",
12    11: "Персонализация - система помнит предпочтения",
13    12: "Обучение - интерфейс становится умнее от использования"
14};

Фаза 4: Wireframing интерактивного интерфейса (15 мин)

Метод: Collaborative Design

Дети рисуют схемы интерфейса с учетом всех принципов:

  • Где располагаются элементы для каждой персоны
  • Какие анимации и переходы будут
  • Как система будет адаптироваться
  • Какие жесты и взаимодействия поддерживать

Занятие 2: “Создание живого интерфейса” ✨

Длительность: 90 минут

Фаза 1: Продвинутый HTML с семантикой (20 мин)

Концепция: “HTML как язык смысла”

  1<!DOCTYPE html>
  2<html lang="ru">
  3<head>
  4    <meta charset="UTF-8">
  5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6    <meta name="description" content="ALEX - Умная система управления классом">
  7    <meta name="theme-color" content="#667eea">
  8    
  9    <!-- PWA мета-теги -->
 10    <link rel="manifest" href="/manifest.json">
 11    <link rel="apple-touch-icon" href="/icon-192.png">
 12    
 13    <title>🤖 ALEX - Ваш умный помощник</title>
 14    
 15    <style>
 16        /* CSS Custom Properties для динамической темизации */
 17        :root {
 18            --primary-color: #667eea;
 19            --secondary-color: #764ba2;
 20            --accent-color: #4ECDC4;
 21            --text-color: white;
 22            --background-opacity: 0.1;
 23            --animation-speed: 0.3s;
 24            --border-radius: 15px;
 25            --shadow-color: rgba(0,0,0,0.2);
 26            
 27            /* Адаптивные размеры */
 28            --button-size: clamp(120px, 15vw, 200px);
 29            --text-size: clamp(14px, 2vw, 18px);
 30            --icon-size: clamp(2rem, 4vw, 4rem);
 31        }
 32        
 33        /* Темы для разных персон */
 34        [data-persona="teacher"] {
 35            --button-size: clamp(150px, 20vw, 250px);
 36            --text-size: clamp(16px, 2.5vw, 20px);
 37            --animation-speed: 0.2s;
 38        }
 39        
 40        [data-persona="student"] {
 41            --accent-color: #FF6B6B;
 42            --animation-speed: 0.4s;
 43            --border-radius: 20px;
 44        }
 45        
 46        [data-persona="janitor"] {
 47            --button-size: clamp(180px, 25vw, 300px);
 48            --text-size: clamp(18px, 3vw, 24px);
 49            --animation-speed: 0.1s;
 50            --background-opacity: 0.2;
 51        }
 52        
 53        * {
 54            margin: 0;
 55            padding: 0;
 56            box-sizing: border-box;
 57        }
 58        
 59        body {
 60            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
 61            background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
 62            min-height: 100vh;
 63            overflow-x: hidden;
 64            transition: all var(--animation-speed) ease;
 65            user-select: none;
 66        }
 67        
 68        /* Адаптивный контейнер */
 69        .container {
 70            max-width: 1200px;
 71            margin: 0 auto;
 72            padding: clamp(15px, 3vw, 30px);
 73            min-height: 100vh;
 74            display: flex;
 75            flex-direction: column;
 76        }
 77        
 78        /* Семантическая шапка */
 79        .system-header {
 80            text-align: center;
 81            margin-bottom: 2rem;
 82            animation: slideInDown 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
 83        }
 84        
 85        .system-title {
 86            font-size: clamp(2rem, 6vw, 4rem);
 87            color: var(--text-color);
 88            margin-bottom: 1rem;
 89            text-shadow: 2px 2px 4px var(--shadow-color);
 90            position: relative;
 91        }
 92        
 93        .system-title::after {
 94            content: '';
 95            position: absolute;
 96            bottom: -10px;
 97            left: 50%;
 98            transform: translateX(-50%);
 99            width: 100px;
100            height: 3px;
101            background: var(--accent-color);
102            border-radius: 2px;
103            animation: expandWidth 1s ease-out 0.5s backwards;
104        }
105        
106        @keyframes expandWidth {
107            from { width: 0; }
108            to { width: 100px; }
109        }
110        
111        /* Адаптивная статус-панель */
112        .status-panel {
113            display: flex;
114            align-items: center;
115            justify-content: center;
116            gap: 1rem;
117            background: rgba(255,255,255, var(--background-opacity));
118            padding: 1rem 2rem;
119            border-radius: var(--border-radius);
120            backdrop-filter: blur(10px);
121            border: 1px solid rgba(255,255,255,0.2);
122            margin-bottom: 2rem;
123            transition: all var(--animation-speed) ease;
124        }
125        
126        .status-panel:hover {
127            transform: translateY(-2px);
128            box-shadow: 0 5px 25px var(--shadow-color);
129        }
130        
131        .status-indicator {
132            display: flex;
133            align-items: center;
134            gap: 0.5rem;
135        }
136        
137        .status-dot {
138            width: 12px;
139            height: 12px;
140            border-radius: 50%;
141            background: var(--accent-color);
142            animation: pulse 2s infinite;
143            position: relative;
144        }
145        
146        .status-dot::after {
147            content: '';
148            position: absolute;
149            width: 100%;
150            height: 100%;
151            border-radius: 50%;
152            background: var(--accent-color);
153            animation: ripple 2s infinite;
154        }
155        
156        @keyframes pulse {
157            0%, 100% { opacity: 1; }
158            50% { opacity: 0.5; }
159        }
160        
161        @keyframes ripple {
162            0% {
163                transform: scale(1);
164                opacity: 0.7;
165            }
166            100% {
167                transform: scale(2);
168                opacity: 0;
169            }
170        }
171        
172        /* Основная область контента */
173        .main-content {
174            flex: 1;
175            display: grid;
176            gap: 2rem;
177            grid-template-columns: 1fr;
178        }
179        
180        @media (min-width: 768px) {
181            .main-content {
182                grid-template-columns: 2fr 1fr;
183            }
184        }
185        
186        /* Интерактивная панель датчиков */
187        .sensors-section {
188            display: grid;
189            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
190            gap: 1.5rem;
191        }
192        
193        .sensor-card {
194            background: rgba(255,255,255, var(--background-opacity));
195            backdrop-filter: blur(15px);
196            border-radius: var(--border-radius);
197            padding: 1.5rem;
198            border: 1px solid rgba(255,255,255,0.2);
199            position: relative;
200            overflow: hidden;
201            cursor: pointer;
202            transition: all var(--animation-speed) cubic-bezier(0.175, 0.885, 0.32, 1.275);
203            
204            /* Подготовка к анимациям */
205            transform-origin: center;
206            will-change: transform;
207        }
208        
209        .sensor-card::before {
210            content: '';
211            position: absolute;
212            top: 0;
213            left: -100%;
214            width: 100%;
215            height: 100%;
216            background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
217            transition: left 0.5s ease;
218            z-index: 1;
219        }
220        
221        .sensor-card:hover::before {
222            left: 100%;
223        }
224        
225        .sensor-card:hover {
226            transform: translateY(-8px) scale(1.02);
227            box-shadow: 0 20px 40px var(--shadow-color);
228            border-color: rgba(255,255,255,0.4);
229        }
230        
231        .sensor-card:active {
232            transform: translateY(-4px) scale(0.98);
233        }
234        
235        /* Семантическая структура датчика */
236        .sensor-icon {
237            font-size: var(--icon-size);
238            margin-bottom: 1rem;
239            display: block;
240            transition: transform var(--animation-speed) ease;
241            position: relative;
242            z-index: 2;
243        }
244        
245        .sensor-card:hover .sensor-icon {
246            transform: scale(1.2) rotate(5deg);
247        }
248        
249        .sensor-name {
250            font-size: calc(var(--text-size) * 1.1);
251            color: rgba(255,255,255,0.9);
252            margin-bottom: 0.5rem;
253            font-weight: 600;
254            position: relative;
255            z-index: 2;
256        }
257        
258        .sensor-value {
259            font-size: calc(var(--text-size) * 1.8);
260            font-weight: bold;
261            color: var(--text-color);
262            margin: 1rem 0;
263            position: relative;
264            z-index: 2;
265            transition: all var(--animation-speed) ease;
266        }
267        
268        .sensor-status {
269            font-size: calc(var(--text-size) * 0.9);
270            color: rgba(255,255,255,0.8);
271            font-style: italic;
272            position: relative;
273            z-index: 2;
274        }
275        
276        /* Визуальный прогресс-бар */
277        .sensor-progress {
278            width: 100%;
279            height: 6px;
280            background: rgba(255,255,255,0.2);
281            border-radius: 3px;
282            overflow: hidden;
283            margin-top: 1rem;
284            position: relative;
285            z-index: 2;
286        }
287        
288        .progress-fill {
289            height: 100%;
290            background: linear-gradient(90deg, var(--accent-color), var(--primary-color));
291            border-radius: 3px;
292            transition: width 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
293            position: relative;
294        }
295        
296        .progress-fill::after {
297            content: '';
298            position: absolute;
299            top: 0;
300            left: 0;
301            right: 0;
302            bottom: 0;
303            background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
304            animation: progressShine 2s infinite;
305        }
306        
307        @keyframes progressShine {
308            0% { transform: translateX(-100%); }
309            100% { transform: translateX(100%); }
310        }
311        
312        /* Панель управления */
313        .controls-section {
314            display: flex;
315            flex-direction: column;
316            gap: 1.5rem;
317        }
318        
319        .section-title {
320            color: var(--text-color);
321            font-size: calc(var(--text-size) * 1.3);
322            margin-bottom: 1rem;
323            text-align: center;
324            position: relative;
325        }
326        
327        .section-title::after {
328            content: '';
329            position: absolute;
330            bottom: -5px;
331            left: 50%;
332            transform: translateX(-50%);
333            width: 50px;
334            height: 2px;
335            background: var(--accent-color);
336            border-radius: 1px;
337        }
338        
339        /* Умные кнопки управления */
340        .control-grid {
341            display: grid;
342            grid-template-columns: repeat(auto-fit, minmax(var(--button-size), 1fr));
343            gap: 1rem;
344        }
345        
346        .smart-button {
347            background: rgba(255,255,255, var(--background-opacity));
348            border: 2px solid rgba(255,255,255,0.3);
349            border-radius: var(--border-radius);
350            padding: 1.5rem 1rem;
351            color: var(--text-color);
352            font-size: var(--text-size);
353            font-weight: 600;
354            cursor: pointer;
355            position: relative;
356            overflow: hidden;
357            transition: all var(--animation-speed) cubic-bezier(0.175, 0.885, 0.32, 1.275);
358            text-align: center;
359            user-select: none;
360            backdrop-filter: blur(10px);
361            
362            /* Подготовка к трансформациям */
363            transform-origin: center;
364            will-change: transform;
365        }
366        
367        .smart-button::before {
368            content: '';
369            position: absolute;
370            top: 0;
371            left: 0;
372            right: 0;
373            bottom: 0;
374            background: radial-gradient(circle at center, rgba(255,255,255,0.3) 0%, transparent 70%);
375            opacity: 0;
376            transition: opacity var(--animation-speed) ease;
377            z-index: 1;
378        }
379        
380        .smart-button:hover::before {
381            opacity: 1;
382        }
383        
384        .smart-button:hover {
385            transform: translateY(-4px) scale(1.05);
386            box-shadow: 0 15px 35px var(--shadow-color);
387            border-color: rgba(255,255,255,0.5);
388            background: rgba(255,255,255, calc(var(--background-opacity) + 0.1));
389        }
390        
391        .smart-button:active {
392            transform: translateY(-2px) scale(0.95);
393            transition: transform 0.1s ease;
394        }
395        
396        /* Содержимое кнопки */
397        .button-content {
398            position: relative;
399            z-index: 2;
400            display: flex;
401            flex-direction: column;
402            align-items: center;
403            gap: 0.5rem;
404        }
405        
406        .button-icon {
407            font-size: calc(var(--text-size) * 1.5);
408            margin-bottom: 0.5rem;
409        }
410        
411        .button-text {
412            font-weight: 600;
413            line-height: 1.2;
414        }
415        
416        .button-description {
417            font-size: calc(var(--text-size) * 0.8);
418            opacity: 0.8;
419            line-height: 1.2;
420        }
421        
422        /* Специальные кнопки */
423        .smart-button.primary {
424            background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
425            border: none;
426            animation: primaryGlow 3s infinite;
427        }
428        
429        @keyframes primaryGlow {
430            0%, 100% {
431                box-shadow: 0 5px 20px rgba(78, 205, 196, 0.4);
432            }
433            50% {
434                box-shadow: 0 5px 30px rgba(78, 205, 196, 0.6);
435            }
436        }
437        
438        .smart-button.danger {
439            border-color: #ff6b6b;
440            color: #ff6b6b;
441        }
442        
443        .smart-button.danger:hover {
444            background: rgba(255, 107, 107, 0.1);
445            border-color: #ff5252;
446        }
447        
448        /* Рипл-эффект для кнопок */
449        .smart-button::after {
450            content: '';
451            position: absolute;
452            top: 50%;
453            left: 50%;
454            width: 0;
455            height: 0;
456            border-radius: 50%;
457            background: rgba(255,255,255,0.4);
458            transform: translate(-50%, -50%);
459            transition: width 0.6s ease, height 0.6s ease;
460            z-index: 1;
461        }
462        
463        .smart-button:active::after {
464            width: 300px;
465            height: 300px;
466        }
467        
468        /* Адаптивность */
469        @media (max-width: 768px) {
470            .main-content {
471                grid-template-columns: 1fr;
472            }
473            
474            .sensors-section {
475                grid-template-columns: 1fr;
476            }
477        }
478        
479        @media (max-width: 480px) {
480            .sensor-card {
481                padding: 1rem;
482            }
483            
484            .smart-button {
485                padding: 1rem 0.5rem;
486            }
487        }
488        
489        /* Темы времени суток */
490        .theme-morning {
491            --primary-color: #ffeaa7;
492            --secondary-color: #fdcb6e;
493            --accent-color: #e17055;
494        }
495        
496        .theme-evening {
497            --primary-color: #6c5ce7;
498            --secondary-color: #a29bfe;
499            --accent-color: #fd79a8;
500        }
501        
502        .theme-night {
503            --primary-color: #2d3436;
504            --secondary-color: #636e72;
505            --accent-color: #00b894;
506        }
507        
508        /* Анимации появления */
509        @keyframes slideInDown {
510            from {
511                transform: translateY(-50px);
512                opacity: 0;
513            }
514            to {
515                transform: translateY(0);
516                opacity: 1;
517            }
518        }
519        
520        @keyframes slideInUp {
521            from {
522                transform: translateY(50px);
523                opacity: 0;
524            }
525            to {
526                transform: translateY(0);
527                opacity: 1;
528            }
529        }
530        
531        @keyframes fadeInScale {
532            from {
533                transform: scale(0.8);
534                opacity: 0;
535            }
536            to {
537                transform: scale(1);
538                opacity: 1;
539            }
540        }
541        
542        /* Применение анимаций */
543        .sensor-card {
544            animation: fadeInScale 0.6s ease-out backwards;
545        }
546        
547        .sensor-card:nth-child(1) { animation-delay: 0.1s; }
548        .sensor-card:nth-child(2) { animation-delay: 0.2s; }
549        .sensor-card:nth-child(3) { animation-delay: 0.3s; }
550        .sensor-card:nth-child(4) { animation-delay: 0.4s; }
551        
552        .smart-button {
553            animation: slideInUp 0.6s ease-out backwards;
554        }
555        
556        .smart-button:nth-child(1) { animation-delay: 0.5s; }
557        .smart-button:nth-child(2) { animation-delay: 0.6s; }
558        .smart-button:nth-child(3) { animation-delay: 0.7s; }
559        .smart-button:nth-child(4) { animation-delay: 0.8s; }
560        .smart-button:nth-child(5) { animation-delay: 0.9s; }
561        .smart-button:nth-child(6) { animation-delay: 1.0s; }
562    </style>
563</head>
564
565<body data-persona="student" id="app">
566    <div class="container">
567        <!-- Семантическая шапка -->
568        <header class="system-header">
569            <h1 class="system-title">🤖 ALEX</h1>
570            <div class="status-panel">
571                <div class="status-indicator">
572                    <div class="status-dot" id="statusDot"></div>
573                    <span id="systemStatus">Подключаюсь к системе...</span>
574                </div>
575                <div class="status-indicator">
576                    <span id="connectionQuality">📶</span>
577                    <span id="batteryLevel">🔋</span>
578                </div>
579            </div>
580        </header>
581        
582        <!-- Основной контент -->
583        <main class="main-content">
584            <!-- Секция датчиков -->
585            <section class="sensors-section">
586                <article class="sensor-card" data-sensor="temperature" role="button" tabindex="0" aria-label="Датчик температуры">
587                    <span class="sensor-icon" aria-hidden="true">🌡️</span>
588                    <h3 class="sensor-name">Температура</h3>
589                    <div class="sensor-value" id="temperature" aria-live="polite">--°C</div>
590                    <p class="sensor-status" id="tempStatus">Загрузка...</p>
591                    <div class="sensor-progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
592                        <div class="progress-fill" id="tempProgress" style="width: 0%"></div>
593                    </div>
594                </article>
595                
596                <article class="sensor-card" data-sensor="light" role="button" tabindex="0" aria-label="Датчик освещенности">
597                    <span class="sensor-icon" aria-hidden="true">💡</span>
598                    <h3 class="sensor-name">Освещенность</h3>
599                    <div class="sensor-value" id="light" aria-live="polite">--%</div>
600                    <p class="sensor-status" id="lightStatus">Загрузка...</p>
601                    <div class="sensor-progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
602                        <div class="progress-fill" id="lightProgress" style="width: 0%"></div>
603                    </div>
604                </article>
605                
606                <article class="sensor-card" data-sensor="sound" role="button" tabindex="0" aria-label="Датчик звука">
607                    <span class="sensor-icon" aria-hidden="true">🔊</span>
608                    <h3 class="sensor-name">Уровень шума</h3>
609                    <div class="sensor-value" id="sound" aria-live="polite">--дБ</div>
610                    <p class="sensor-status" id="soundStatus">Загрузка...</p>
611                    <div class="sensor-progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
612                        <div class="progress-fill" id="soundProgress" style="width: 0%"></div>
613                    </div>
614                </article>
615                
616                <article class="sensor-card" data-sensor="motion" role="button" tabindex="0" aria-label="Датчик движения">
617                    <span class="sensor-icon" aria-hidden="true">🚶</span>
618                    <h3 class="sensor-name">Движение</h3>
619                    <div class="sensor-value" id="motion" aria-live="polite">--</div>
620                    <p class="sensor-status" id="motionStatus">Загрузка...</p>
621                    <div class="sensor-progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
622                        <div class="progress-fill" id="motionProgress" style="width: 0%"></div>
623                    </div>
624                </article>
625            </section>
626            
627            <!-- Панель управления -->
628            <aside class="controls-section">
629                <h2 class="section-title">🎛️ Умное управление</h2>
630                <div class="control-grid">
631                    <button class="smart-button primary" data-mode="comfort" data-description="Идеальная температура и освещение">
632                        <div class="button-content">
633                            <span class="button-icon">😌</span>
634                            <span class="button-text">Комфорт</span>
635                            <span class="button-description">Создать уют</span>
636                        </div>
637                    </button>
638                    
639                    <button class="smart-button" data-mode="eco" data-description="Экономия энергии с умом">
640                        <div class="button-content">
641                            <span class="button-icon">🌱</span>
642                            <span class="button-text">Эко-режим</span>
643                            <span class="button-description">Сберечь планету</span>
644                        </div>
645                    </button>
646                    
647                    <button class="smart-button" data-mode="focus" data-description="Оптимальные условия для концентрации">
648                        <div class="button-content">
649                            <span class="button-icon">🎯</span>
650                            <span class="button-text">Фокус</span>
651                            <span class="button-description">Время учиться</span>
652                        </div>
653                    </button>
654                    
655                    <button class="smart-button" data-mode="party" data-description="Атмосфера для веселья">
656                        <div class="button-content">
657                            <span class="button-icon">🎉</span>
658                            <span class="button-text">Вечеринка</span>
659                            <span class="button-description">Время радости</span>
660                        </div>
661                    </button>
662                    
663                    <button class="smart-button" data-mode="presentation" data-description="Подготовка к показу">
664                        <div class="button-content">
665                            <span class="button-icon">📽️</span>
666                            <span class="button-text">Презентация</span>
667                            <span class="button-description">Все внимание</span>
668                        </div>
669                    </button>
670                    
671                    <button class="smart-button danger" data-mode="emergency" data-description="Экстренное отключение">
672                        <div class="button-content">
673                            <span class="button-icon">🚨</span>
674                            <span class="button-text">Экстренный</span>
675                            <span class="button-description">Все выключить</span>
676                        </div>
677                    </button>
678                </div>
679            </aside>
680        </main>
681    </div>
682</body>
683</html>

Фаза 2: Продвинутый JavaScript для интерактивности (35 мин)

Концепция: “JavaScript как мозг интерфейса”

   1// === КЛАСС УМНОГО ИНТЕРФЕЙСА ===
   2class SmartInterface {
   3    constructor() {
   4        this.sensorData = {};
   5        this.userPreferences = this.loadUserPreferences();
   6        this.currentPersona = this.detectPersona();
   7        this.socket = null;
   8        this.isOnline = false;
   9        this.gestureRecognizer = null;
  10        
  11        this.init();
  12    }
  13    
  14    async init() {
  15        console.log('🚀 Инициализация умного интерфейса...');
  16        
  17        // Определяем возможности устройства
  18        await this.detectDeviceCapabilities();
  19        
  20        // Настраиваем персону
  21        this.applyPersona(this.currentPersona);
  22        
  23        // Подключаемся к системе
  24        this.connectToSystem();
  25        
  26        // Инициализируем взаимодействия
  27        this.setupInteractions();
  28        
  29        // Запускаем адаптивную логику
  30        this.startAdaptiveEngine();
  31        
  32        console.log('✅ Умный интерфейс готов!');
  33        this.showWelcomeMessage();
  34    }
  35    
  36    // === ОПРЕДЕЛЕНИЕ ВОЗМОЖНОСТЕЙ УСТРОЙСТВА ===
  37    async detectDeviceCapabilities() {
  38        const capabilities = {
  39            vibration: 'vibrate' in navigator,
  40            geolocation: 'geolocation' in navigator,
  41            battery: 'getBattery' in navigator,
  42            deviceMotion: 'DeviceMotionEvent' in window,
  43            touchGestures: 'ontouchstart' in window,
  44            voiceRecognition: 'SpeechRecognition' in window || 'webkitSpeechRecognition' in window,
  45            notifications: 'Notification' in window,
  46            serviceWorker: 'serviceWorker' in navigator
  47        };
  48        
  49        this.deviceCapabilities = capabilities;
  50        
  51        // Запрашиваем разрешения
  52        if (capabilities.notifications && Notification.permission !== 'granted') {
  53            await Notification.requestPermission();
  54        }
  55        
  56        // Получаем информацию о батарее
  57        if (capabilities.battery) {
  58            try {
  59                this.battery = await navigator.getBattery();
  60                this.updateBatteryIndicator();
  61                this.battery.addEventListener('levelchange', () => this.updateBatteryIndicator());
  62            } catch (e) {
  63                console.log('Информация о батарее недоступна');
  64            }
  65        }
  66        
  67        console.log('📱 Возможности устройства:', capabilities);
  68    }
  69    
  70    // === ОПРЕДЕЛЕНИЕ ПЕРСОНЫ ПОЛЬЗОВАТЕЛЯ ===
  71    detectPersona() {
  72        const hour = new Date().getHours();
  73        const userAgent = navigator.userAgent;
  74        const screenSize = window.innerWidth;
  75        
  76        // Простая эвристика определения типа пользователя
  77        if (hour >= 7 && hour <= 9) {
  78            return 'teacher'; // Утром обычно учителя
  79        } else if (hour >= 16 && hour <= 18) {
  80            return 'janitor'; // Вечером техперсонал
  81        } else if (screenSize < 768) {
  82            return 'student'; // Мобильные устройства чаще у учеников
  83        }
  84        
  85        return 'student'; // По умолчанию
  86    }
  87    
  88    // === ПРИМЕНЕНИЕ ПЕРСОНЫ ===
  89    applyPersona(persona) {
  90        document.body.setAttribute('data-persona', persona);
  91        
  92        const personaSettings = {
  93            teacher: {
  94                animationSpeed: '0.2s',
  95                buttonSize: '200px',
  96                textSize: '18px',
  97                shortcuts: true,
  98                voiceControl: true
  99            },
 100            student: {
 101                animationSpeed: '0.4s',
 102                buttonSize: '150px',
 103                textSize: '16px',
 104                richAnimations: true,
 105                gameElements: true
 106            },
 107            janitor: {
 108                animationSpeed: '0.1s',
 109                buttonSize: '250px',
 110                textSize: '20px',
 111                highContrast: true,
 112                largeTargets: true
 113            }
 114        };
 115        
 116        const settings = personaSettings[persona];
 117        if (settings) {
 118            // Применяем CSS переменные для персоны
 119            Object.entries(settings).forEach(([key, value]) => {
 120                if (typeof value === 'string' && value.includes('px') || value.includes('s')) {
 121                    document.documentElement.style.setProperty(`--${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`, value);
 122                }
 123            });
 124            
 125            // Специфичные настройки
 126            if (settings.shortcuts) this.enableKeyboardShortcuts();
 127            if (settings.voiceControl) this.enableVoiceControl();
 128            if (settings.richAnimations) this.enableRichAnimations();
 129            if (settings.gameElements) this.enableGameElements();
 130        }
 131        
 132        console.log(`👤 Применена персона: ${persona}`);
 133    }
 134    
 135    // === ПОДКЛЮЧЕНИЕ К СИСТЕМЕ ===
 136    connectToSystem() {
 137        const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
 138        const wsUrl = `${wsProtocol}//${window.location.hostname}:81`;
 139        
 140        try {
 141            this.socket = new WebSocket(wsUrl);
 142            
 143            this.socket.onopen = () => {
 144                this.isOnline = true;
 145                this.updateConnectionStatus('🟢 Подключен к системе', 'online');
 146                console.log('🔗 WebSocket подключен');
 147                
 148                // Отправляем информацию о пользователе
 149                this.socket.send(JSON.stringify({
 150                    type: 'user_info',
 151                    persona: this.currentPersona,
 152                    capabilities: this.deviceCapabilities,
 153                    preferences: this.userPreferences
 154                }));
 155            };
 156            
 157            this.socket.onmessage = (event) => {
 158                this.handleSystemMessage(event.data);
 159            };
 160            
 161            this.socket.onclose = () => {
 162                this.isOnline = false;
 163                this.updateConnectionStatus('🔴 Переподключение...', 'reconnecting');
 164                console.log('📡 WebSocket отключен, переподключаюсь...');
 165                
 166                // Автоматическое переподключение
 167                setTimeout(() => this.connectToSystem(), 3000);
 168            };
 169            
 170            this.socket.onerror = (error) => {
 171                console.error('❌ Ошибка WebSocket:', error);
 172                this.updateConnectionStatus('🔴 Ошибка соединения', 'error');
 173            };
 174            
 175        } catch (error) {
 176            console.error('❌ Не удалось подключиться к системе:', error);
 177            this.updateConnectionStatus('🔴 Офлайн режим', 'offline');
 178        }
 179    }
 180    
 181    // === ОБРАБОТКА СООБЩЕНИЙ ОТ СИСТЕМЫ ===
 182    handleSystemMessage(message) {
 183        try {
 184            const data = JSON.parse(message);
 185            
 186            switch (data.type) {
 187                case 'sensor_update':
 188                    this.updateSensorData(data);
 189                    break;
 190                case 'system_alert':
 191                    this.showAlert(data.message, data.level);
 192                    break;
 193                case 'suggestion':
 194                    this.showSuggestion(data.suggestion);
 195                    break;
 196                case 'mood_update':
 197                    this.updateSystemMood(data.mood);
 198                    break;
 199                default:
 200                    console.log('💬 Сообщение от системы:', message);
 201            }
 202        } catch (e) {
 203            // Обычное текстовое сообщение
 204            console.log('📝 Текст от системы:', message);
 205        }
 206    }
 207    
 208    // === ОБНОВЛЕНИЕ ДАННЫХ ДАТЧИКОВ ===
 209    updateSensorData(data) {
 210        const oldData = { ...this.sensorData };
 211        this.sensorData = data;
 212        
 213        // Анимированное обновление каждого датчика
 214        this.animateValueUpdate('temperature', data.temperature + '°C', oldData.temperature);
 215        this.animateValueUpdate('light', Math.round((data.light / 4095) * 100) + '%', oldData.light);
 216        this.animateValueUpdate('sound', Math.round((data.sound / 4095) * 100) + 'дБ', oldData.sound);
 217        this.animateValueUpdate('motion', data.motion ? 'Есть' : 'Нет', oldData.motion);
 218        
 219        // Обновляем прогресс-бары
 220        this.updateProgressBar('tempProgress', data.temperature, 0, 40);
 221        this.updateProgressBar('lightProgress', (data.light / 4095) * 100, 0, 100);
 222        this.updateProgressBar('soundProgress', (data.sound / 4095) * 100, 0, 100);
 223        this.updateProgressBar('motionProgress', data.motion ? 100 : 0, 0, 100);
 224        
 225        // Обновляем статусы
 226        this.updateSensorStatus('tempStatus', this.getTemperatureStatus(data.temperature));
 227        this.updateSensorStatus('lightStatus', this.getLightStatus((data.light / 4095) * 100));
 228        this.updateSensorStatus('soundStatus', this.getSoundStatus((data.sound / 4095) * 100));
 229        this.updateSensorStatus('motionStatus', this.getMotionStatus(data.motion));
 230        
 231        // Проверяем нужны ли предложения
 232        this.checkForSuggestions(data);
 233        
 234        // Адаптируем интерфейс под условия
 235        this.adaptInterfaceToConditions(data);
 236    }
 237    
 238    // === АНИМИРОВАННОЕ ОБНОВЛЕНИЕ ЗНАЧЕНИЙ ===
 239    animateValueUpdate(elementId, newValue, oldValue) {
 240        const element = document.getElementById(elementId);
 241        const currentValue = element.textContent;
 242        
 243        if (currentValue !== newValue) {
 244            // Создаем эффект изменения
 245            element.style.transform = 'scale(1.1)';
 246            element.style.color = '#FFD93D';
 247            
 248            // Добавляем вибрацию на мобильных
 249            if (this.deviceCapabilities.vibration && oldValue !== undefined) {
 250                navigator.vibrate(50);
 251            }
 252            
 253            setTimeout(() => {
 254                element.textContent = newValue;
 255                element.style.transform = 'scale(1)';
 256                element.style.color = '';
 257                
 258                // Подсвечиваем родительскую карточку
 259                const card = element.closest('.sensor-card');
 260                if (card) {
 261                    card.style.boxShadow = '0 0 30px rgba(255, 217, 61, 0.5)';
 262                    setTimeout(() => {
 263                        card.style.boxShadow = '';
 264                    }, 800);
 265                }
 266            }, 200);
 267            
 268            // Обновляем ARIA атрибуты для скринридеров
 269            element.setAttribute('aria-live', 'polite');
 270        }
 271    }
 272    
 273    // === ОБНОВЛЕНИЕ ПРОГРЕСС-БАРОВ ===
 274    updateProgressBar(progressId, value, min, max) {
 275        const progressElement = document.getElementById(progressId);
 276        const percentage = Math.max(0, Math.min(100, ((value - min) / (max - min)) * 100));
 277        
 278        // Плавная анимация изменения
 279        progressElement.style.width = percentage + '%';
 280        
 281        // Обновляем ARIA атрибуты
 282        const progressBar = progressElement.closest('[role="progressbar"]');
 283        if (progressBar) {
 284            progressBar.setAttribute('aria-valuenow', Math.round(percentage));
 285        }
 286    }
 287    
 288    // === УМНЫЕ СТАТУСЫ ДАТЧИКОВ ===
 289    getTemperatureStatus(temp) {
 290        if (temp < 18) return { message: '🥶 Холодно! Предлагаю включить обогрев', color: '#4FC3F7', action: 'heating' };
 291        if (temp > 26) return { message: '🔥 Жарко! Нужно охлаждение', color: '#FF7043', action: 'cooling' };
 292        if (temp >= 20 && temp <= 24) return { message: '😌 Идеальная температура', color: '#66BB6A', action: null };
 293        return { message: '🌡️ Нормальная температура', color: '#FFD54F', action: null };
 294    }
 295    
 296    getLightStatus(light) {
 297        if (light < 20) return { message: '🌙 Темно! Включить освещение?', color: '#9575CD', action: 'lighting' };
 298        if (light > 80) return { message: '☀️ Очень светло', color: '#FFB74D', action: null };
 299        return { message: '👍 Комфортное освещение', color: '#81C784', action: null };
 300    }
 301    
 302    getSoundStatus(sound) {
 303        if (sound < 20) return { message: '🤫 Очень тихо', color: '#4DB6AC', action: null };
 304        if (sound > 70) return { message: '📢 Шумно! Попросить потише?', color: '#E57373', action: 'noise_control' };
 305        return { message: '🎵 Комфортный уровень звука', color: '#AED581', action: null };
 306    }
 307    
 308    getMotionStatus(motion) {
 309        return motion ? 
 310            { message: '👥 Люди в классе', color: '#64B5F6', action: null } : 
 311            { message: '🏫 Класс пустой - эко-режим?', color: '#90A4AE', action: 'eco_mode' };
 312    }
 313    
 314    // === ОБНОВЛЕНИЕ СТАТУСОВ ===
 315    updateSensorStatus(statusId, status) {
 316        const statusElement = document.getElementById(statusId);
 317        statusElement.textContent = status.message;
 318        statusElement.style.color = status.color;
 319        
 320        // Если есть предлагаемое действие, показываем кнопку
 321        if (status.action) {
 322            this.showActionSuggestion(statusId, status.action, status.message);
 323        }
 324    }
 325    
 326    // === ПРЕДЛОЖЕНИЯ ДЕЙСТВИЙ ===
 327    showActionSuggestion(statusId, action, message) {
 328        const statusElement = document.getElementById(statusId);
 329        const existingSuggestion = statusElement.parentElement.querySelector('.action-suggestion');
 330        
 331        if (existingSuggestion) {
 332            existingSuggestion.remove();
 333        }
 334        
 335        const suggestionButton = document.createElement('button');
 336        suggestionButton.className = 'action-suggestion';
 337        suggestionButton.innerHTML = '✨ Исправить';
 338        suggestionButton.style.cssText = `
 339            margin-top: 8px;
 340            padding: 6px 12px;
 341            background: rgba(255,255,255,0.2);
 342            border: 1px solid rgba(255,255,255,0.3);
 343            border-radius: 15px;
 344            color: white;
 345            font-size: 12px;
 346            cursor: pointer;
 347            transition: all 0.3s ease;
 348        `;
 349        
 350        suggestionButton.addEventListener('click', () => {
 351            this.executeQuickAction(action);
 352            suggestionButton.remove();
 353        });
 354        
 355        suggestionButton.addEventListener('mouseenter', () => {
 356            suggestionButton.style.background = 'rgba(255,255,255,0.3)';
 357            suggestionButton.style.transform = 'scale(1.05)';
 358        });
 359        
 360        suggestionButton.addEventListener('mouseleave', () => {
 361            suggestionButton.style.background = 'rgba(255,255,255,0.2)';
 362            suggestionButton.style.transform = 'scale(1)';
 363        });
 364        
 365        statusElement.parentElement.appendChild(suggestionButton);
 366    }
 367    
 368    // === БЫСТРЫЕ ДЕЙСТВИЯ ===
 369    async executeQuickAction(action) {
 370        const actionMap = {
 371            'heating': () => this.executeSmartMode('comfort'),
 372            'cooling': () => this.executeSmartMode('cool'),
 373            'lighting': () => this.executeSmartMode('bright'),
 374            'noise_control': () => this.showNoiseAlert(),
 375            'eco_mode': () => this.executeSmartMode('eco')
 376        };
 377        
 378        if (actionMap[action]) {
 379            await actionMap[action]();
 380        }
 381    }
 382    
 383    // === НАСТРОЙКА ВЗАИМОДЕЙСТВИЙ ===
 384    setupInteractions() {
 385        // Обработчики кнопок управления
 386        document.querySelectorAll('.smart-button').forEach(button => {
 387            this.setupSmartButton(button);
 388        });
 389        
 390        // Обработчики карточек датчиков
 391        document.querySelectorAll('.sensor-card').forEach(card => {
 392            this.setupSensorCard(card);
 393        });
 394        
 395        // Клавиатурные сокращения
 396        this.setupKeyboardShortcuts();
 397        
 398        // Жесты
 399        if (this.deviceCapabilities.touchGestures) {
 400            this.setupTouchGestures();
 401        }
 402        
 403        // Голосовое управление
 404        if (this.deviceCapabilities.voiceRecognition) {
 405            this.setupVoiceControl();
 406        }
 407        
 408        // Адаптация к ориентации устройства
 409        if (this.deviceCapabilities.deviceMotion) {
 410            this.setupOrientationAdaptation();
 411        }
 412    }
 413    
 414    // === НАСТРОЙКА УМНЫХ КНОПОК ===
 415    setupSmartButton(button) {
 416        const mode = button.dataset.mode;
 417        
 418        button.addEventListener('click', async (e) => {
 419            e.preventDefault();
 420            await this.executeSmartMode(mode, button);
 421        });
 422        
 423        // Предварительный просмотр при наведении
 424        button.addEventListener('mouseenter', () => {
 425            this.showModePreview(mode, button);
 426        });
 427        
 428        button.addEventListener('mouseleave', () => {
 429            this.hideModePreview();
 430        });
 431        
 432        // Поддержка клавиатуры
 433        button.addEventListener('keydown', (e) => {
 434            if (e.key === 'Enter' || e.key === ' ') {
 435                e.preventDefault();
 436                button.click();
 437            }
 438        });
 439    }
 440    
 441    // === ВЫПОЛНЕНИЕ УМНОГО РЕЖИМА ===
 442    async executeSmartMode(mode, buttonElement = null) {
 443        if (buttonElement) {
 444            // Визуальная обратная связь
 445            const originalContent = buttonElement.innerHTML;
 446            buttonElement.innerHTML = '<div class="button-content"><span class="button-icon">⏳</span><span class="button-text">Применяю...</span></div>';
 447            buttonElement.disabled = true;
 448            
 449            // Тактильная обратная связь
 450            if (this.deviceCapabilities.vibration) {
 451                navigator.vibrate([100, 50, 100]);
 452            }
 453        }
 454        
 455        try {
 456            const response = await fetch(`/api/smart-mode/${mode}`, {
 457                method: 'POST',
 458                headers: { 'Content-Type': 'application/json' },
 459                body: JSON.stringify({
 460                    currentSensors: this.sensorData,
 461                    userPreferences: this.userPreferences,
 462                    persona: this.currentPersona,
 463                    deviceCapabilities: this.deviceCapabilities,
 464                    timestamp: Date.now()
 465                })
 466            });
 467            
 468            const result = await response.json();
 469            
 470            if (result.success) {
 471                if (buttonElement) {
 472                    buttonElement.innerHTML = '<div class="button-content"><span class="button-icon">✅</span><span class="button-text">Готово!</span></div>';
 473                    buttonElement.style.background = 'linear-gradient(135deg, #4CAF50, #45a049)';
 474                }
 475                
 476                // Показываем детали изменений
 477                this.showModeResult(mode, result);
 478                
 479                // Обучаем систему на выборе пользователя
 480                this.learnFromUserChoice(mode, this.sensorData);
 481                
 482                // Показываем уведомление
 483                this.showNotification(`Режим "${this.getModeTitle(mode)}" активирован`, 'success');
 484                
 485            } else {
 486                throw new Error(result.message || 'Ошибка выполнения');
 487            }
 488            
 489        } catch (error) {
 490            console.error('❌ Ошибка выполнения режима:', error);
 491            
 492            if (buttonElement) {
 493                buttonElement.innerHTML = '<div class="button-content"><span class="button-icon">❌</span><span class="button-text">Ошибка</span></div>';
 494                buttonElement.style.background = 'linear-gradient(135deg, #f44336, #d32f2f)';
 495            }
 496            
 497            this.showNotification('Ошибка выполнения команды', 'error');
 498        } finally {
 499            // Возвращаем кнопку в исходное состояние
 500            if (buttonElement) {
 501                setTimeout(() => {
 502                    buttonElement.innerHTML = buttonElement.dataset.originalContent || originalContent;
 503                    buttonElement.style.background = '';
 504                    buttonElement.disabled = false;
 505                }, 2000);
 506            }
 507        }
 508    }
 509    
 510    // === ПРЕДВАРИТЕЛЬНЫЙ ПРОСМОТР РЕЖИМА ===
 511    showModePreview(mode, button) {
 512        const preview = this.getModePreview(mode);
 513        
 514        // Создаем всплывающую подсказку
 515        const tooltip = document.createElement('div');
 516        tooltip.className = 'mode-preview-tooltip';
 517        tooltip.innerHTML = `
 518            <h4>${preview.title}</h4>
 519            <p>${preview.description}</p>
 520            <ul>
 521                ${preview.effects.map(effect => `<li>${effect}</li>`).join('')}
 522            </ul>
 523        `;
 524        
 525        tooltip.style.cssText = `
 526            position: absolute;
 527            bottom: 100%;
 528            left: 50%;
 529            transform: translateX(-50%);
 530            background: rgba(0,0,0,0.9);
 531            color: white;
 532            padding: 15px;
 533            border-radius: 10px;
 534            font-size: 14px;
 535            max-width: 250px;
 536            z-index: 1000;
 537            animation: fadeInUp 0.3s ease;
 538            box-shadow: 0 5px 20px rgba(0,0,0,0.3);
 539        `;
 540        
 541        button.style.position = 'relative';
 542        button.appendChild(tooltip);
 543    }
 544    
 545    hideModePreview() {
 546        document.querySelectorAll('.mode-preview-tooltip').forEach(tooltip => {
 547            tooltip.style.animation = 'fadeOut 0.2s ease';
 548            setTimeout(() => tooltip.remove(), 200);
 549        });
 550    }
 551    
 552    getModePreview(mode) {
 553        const previews = {
 554            comfort: {
 555                title: '😌 Комфортный режим',
 556                description: 'Создаю идеальные условия для отдыха и работы',
 557                effects: ['🌡️ Температура 22°C', '💡 Мягкое освещение', '🔇 Контроль шума']
 558            },
 559            eco: {
 560                title: '🌱 Эко-режим',
 561                description: 'Экономлю энергию с умом',
 562                effects: ['⚡ Снижение потребления на 40%', '🌍 Забота об экологии', '💚 Умная оптимизация']
 563            },
 564            focus: {
 565                title: '🎯 Режим концентрации',
 566                description: 'Оптимальные условия для учебы',
 567                effects: ['🧠 Температура для мозга 21°C', '📚 Яркое освещение', '🤫 Минимум отвлечений']
 568            },
 569            party: {
 570                title: '🎉 Вечеринка',
 571                description: 'Создаю атмосферу веселья',
 572                effects: ['🌟 Яркое освещение', '🎵 Разрешен шум', '💃 Энергичная атмосфера']
 573            },
 574            presentation: {
 575                title: '📽️ Презентация',
 576                description: 'Готовлю класс для показа',
 577                effects: ['🔅 Приглушенный свет', '👥 Комфорт для аудитории', '📊 Фокус на экране']
 578            }
 579        };
 580        
 581        return previews[mode] || { title: mode, description: 'Специальный режим', effects: [] };
 582    }
 583    
 584
 585
 586    // === НАСТРОЙКА КАРТОЧЕК ДАТЧИКОВ ===
 587    setupSensorCard(card) {
 588        const sensorType = card.dataset.sensor;
 589        
 590        card.addEventListener('click', () => {
 591            this.showSensorDetails(sensorType);
 592        });
 593        
 594        // Двойной клик для быстрой калибровки
 595        card.addEventListener('dblclick', () => {
 596            this.calibrateSensor(sensorType);
 597        });
 598        
 599        // Поддержка клавиатуры
 600        card.addEventListener('keydown', (e) => {
 601            if (e.key === 'Enter') {
 602                this.showSensorDetails(sensorType);
 603            }
 604        });
 605    }
 606    
 607    showSensorDetails(sensorType) {
 608        // Показываем детальную аналитику датчика
 609        console.log(`📊 Детали датчика: ${sensorType}`);
 610        
 611        const modal = this.createModal(`
 612            <h3>📊 Аналитика датчика: ${this.getSensorName(sensorType)}</h3>
 613            <div class="sensor-analytics">
 614                <div class="chart-container">
 615                    <canvas id="sensorChart" width="300" height="200"></canvas>
 616                </div>
 617                <div class="stats">
 618                    <p><strong>Текущее значение:</strong> <span id="currentValue">--</span></p>
 619                    <p><strong>Среднее за день:</strong> <span id="avgValue">--</span></p>
 620                    <p><strong>Минимум:</strong> <span id="minValue">--</span></p>
 621                    <p><strong>Максимум:</strong> <span id="maxValue">--</span></p>
 622                </div>
 623                <button onclick="smartInterface.calibrateSensor('${sensorType}')" class="calibrate-btn">
 624                    🔧 Калибровать датчик
 625                </button>
 626            </div>
 627        `);
 628        
 629        // Здесь можно добавить реальную визуализацию данных
 630        this.drawSensorChart(sensorType);
 631    }
 632    
 633    getSensorName(type) {
 634        const names = {
 635            temperature: '🌡️ Температура',
 636            light: '💡 Освещенность', 
 637            sound: '🔊 Звук',
 638            motion: '🚶 Движение'
 639        };
 640        return names[type] || type;
 641    }
 642    
 643    // === ГОЛОСОВОЕ УПРАВЛЕНИЕ ===
 644    setupVoiceControl() {
 645        if (!this.deviceCapabilities.voiceRecognition) return;
 646        
 647        const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
 648        this.recognition = new SpeechRecognition();
 649        
 650        this.recognition.lang = 'ru-RU';
 651        this.recognition.continuous = false;
 652        this.recognition.interimResults = false;
 653        
 654        this.recognition.onresult = (event) => {
 655            const command = event.results[0][0].transcript.toLowerCase();
 656            this.processVoiceCommand(command);
 657        };
 658        
 659        // Кнопка активации голосового управления
 660        const voiceButton = this.createVoiceButton();
 661        document.body.appendChild(voiceButton);
 662    }
 663    
 664    processVoiceCommand(command) {
 665        console.log(`🎤 Голосовая команда: ${command}`);
 666        
 667        const commandMap = {
 668            'комфорт': () => this.executeSmartMode('comfort'),
 669            'эко режим': () => this.executeSmartMode('eco'),
 670            'фокус': () => this.executeSmartMode('focus'),
 671            'вечеринка': () => this.executeSmartMode('party'),
 672            'презентация': () => this.executeSmartMode('presentation'),
 673            'включить свет': () => this.executeQuickAction('lighting'),
 674            'включить обогрев': () => this.executeQuickAction('heating'),
 675            'статус системы': () => this.showSystemStatus()
 676        };
 677        
 678        for (const [pattern, action] of Object.entries(commandMap)) {
 679            if (command.includes(pattern)) {
 680                action();
 681                this.speak(`Выполняю команду: ${pattern}`);
 682                return;
 683            }
 684        }
 685        
 686        this.speak('Команда не распознана. Повторите, пожалуйста.');
 687    }
 688    
 689    speak(text) {
 690        const utterance = new SpeechSynthesisUtterance(text);
 691        utterance.lang = 'ru-RU';
 692        speechSynthesis.speak(utterance);
 693    }
 694    
 695    // === ЖЕСТОВОЕ УПРАВЛЕНИЕ ===
 696    setupTouchGestures() {
 697        let startX, startY, endX, endY;
 698        
 699        document.addEventListener('touchstart', (e) => {
 700            startX = e.touches[0].clientX;
 701            startY = e.touches[0].clientY;
 702        });
 703        
 704        document.addEventListener('touchend', (e) => {
 705            endX = e.changedTouches[0].clientX;
 706            endY = e.changedTouches[0].clientY;
 707            
 708            this.handleGesture(startX, startY, endX, endY);
 709        });
 710    }
 711    
 712    handleGesture(startX, startY, endX, endY) {
 713        const deltaX = endX - startX;
 714        const deltaY = endY - startY;
 715        const threshold = 100;
 716        
 717        if (Math.abs(deltaX) > threshold || Math.abs(deltaY) > threshold) {
 718            if (Math.abs(deltaX) > Math.abs(deltaY)) {
 719                // Горизонтальный свайп
 720                if (deltaX > 0) {
 721                    this.executeSmartMode('comfort'); // Свайп вправо = комфорт
 722                } else {
 723                    this.executeSmartMode('eco'); // Свайп влево = эко
 724                }
 725            } else {
 726                // Вертикальный свайп
 727                if (deltaY < 0) {
 728                    this.showSystemStatus(); // Свайп вверх = статус
 729                } else {
 730                    this.hideAllModals(); // Свайп вниз = закрыть
 731                }
 732            }
 733        }
 734    }
 735    
 736    // === АДАПТАЦИЯ К УСЛОВИЯМ ===
 737    adaptInterfaceToConditions(data) {
 738        // Меняем тему в зависимости от времени суток
 739        const hour = new Date().getHours();
 740        let theme = 'default';
 741        
 742        if (hour >= 6 && hour < 12) {
 743            theme = 'morning';
 744        } else if (hour >= 12 && hour < 18) {
 745            theme = 'day';
 746        } else if (hour >= 18 && hour < 22) {
 747            theme = 'evening';
 748        } else {
 749            theme = 'night';
 750        }
 751        
 752        document.body.className = `theme-${theme}`;
 753        
 754        // Адаптируем яркость интерфейса к освещенности
 755        const lightLevel = (data.light / 4095) * 100;
 756        if (lightLevel < 30) {
 757            document.documentElement.style.setProperty('--background-opacity', '0.05');
 758            document.documentElement.style.setProperty('--text-color', '#ffffff');
 759        } else {
 760            document.documentElement.style.setProperty('--background-opacity', '0.1');
 761            document.documentElement.style.setProperty('--text-color', '#ffffff');
 762        }
 763    }
 764    
 765    // === УВЕДОМЛЕНИЯ И МОДАЛЬНЫЕ ОКНА ===
 766    showNotification(message, type = 'info') {
 767        const notification = document.createElement('div');
 768        notification.className = `notification ${type}`;
 769        notification.innerHTML = `
 770            <div class="notification-content">
 771                <span class="notification-icon">${this.getNotificationIcon(type)}</span>
 772                <span class="notification-text">${message}</span>
 773                <button class="notification-close" onclick="this.parentElement.parentElement.remove()">×</button>
 774            </div>
 775        `;
 776        
 777        notification.style.cssText = `
 778            position: fixed;
 779            top: 20px;
 780            right: 20px;
 781            background: ${this.getNotificationColor(type)};
 782            color: white;
 783            padding: 15px 20px;
 784            border-radius: 10px;
 785            box-shadow: 0 5px 20px rgba(0,0,0,0.3);
 786            z-index: 1000;
 787            animation: slideInRight 0.5s ease;
 788            max-width: 300px;
 789        `;
 790        
 791        document.body.appendChild(notification);
 792        
 793        // Автоудаление через 5 секунд
 794        setTimeout(() => {
 795            if (notification.parentElement) {
 796                notification.style.animation = 'slideOutRight 0.5s ease';
 797                setTimeout(() => notification.remove(), 500);
 798            }
 799        }, 5000);
 800        
 801        // Push-уведомление если разрешено
 802        if (this.deviceCapabilities.notifications && Notification.permission === 'granted') {
 803            new Notification('🤖 ALEX', {
 804                body: message,
 805                icon: '/icon-192.png'
 806            });
 807        }
 808    }
 809    
 810    getNotificationIcon(type) {
 811        const icons = {
 812            success: '✅',
 813            error: '❌',
 814            warning: '⚠️',
 815            info: 'ℹ️'
 816        };
 817        return icons[type] || 'ℹ️';
 818    }
 819    
 820    getNotificationColor(type) {
 821        const colors = {
 822            success: 'linear-gradient(135deg, #4CAF50, #45a049)',
 823            error: 'linear-gradient(135deg, #f44336, #d32f2f)',
 824            warning: 'linear-gradient(135deg, #ff9800, #f57c00)',
 825            info: 'linear-gradient(135deg, #2196F3, #1976D2)'
 826        };
 827        return colors[type] || colors.info;
 828    }
 829    
 830    createModal(content) {
 831        const modal = document.createElement('div');
 832        modal.className = 'modal-overlay';
 833        modal.innerHTML = `
 834            <div class="modal-content">
 835                <button class="modal-close" onclick="this.closest('.modal-overlay').remove()">×</button>
 836                ${content}
 837            </div>
 838        `;
 839        
 840        modal.style.cssText = `
 841            position: fixed;
 842            top: 0;
 843            left: 0;
 844            width: 100%;
 845            height: 100%;
 846            background: rgba(0,0,0,0.8);
 847            display: flex;
 848            justify-content: center;
 849            align-items: center;
 850            z-index: 2000;
 851            animation: fadeIn 0.3s ease;
 852        `;
 853        
 854        document.body.appendChild(modal);
 855        return modal;
 856    }
 857    
 858    // === СОХРАНЕНИЕ ПРЕДПОЧТЕНИЙ ===
 859    saveUserPreferences() {
 860        localStorage.setItem('alexUserPreferences', JSON.stringify(this.userPreferences));
 861    }
 862    
 863    loadUserPreferences() {
 864        const stored = localStorage.getItem('alexUserPreferences');
 865        return stored ? JSON.parse(stored) : {
 866            preferredMode: 'comfort',
 867            notifications: true,
 868            voiceControl: false,
 869            animations: true,
 870            theme: 'auto'
 871        };
 872    }
 873    
 874    learnFromUserChoice(mode, sensorData) {
 875        // Обучение на основе выбора пользователя
 876        this.userPreferences.preferredMode = mode;
 877        this.userPreferences.lastUsed = Date.now();
 878        
 879        // Запоминаем контекст выбора
 880        if (!this.userPreferences.contextualChoices) {
 881            this.userPreferences.contextualChoices = [];
 882        }
 883        
 884        this.userPreferences.contextualChoices.push({
 885            mode: mode,
 886            temperature: sensorData.temperature,
 887            light: sensorData.light,
 888            time: new Date().getHours(),
 889            timestamp: Date.now()
 890        });
 891        
 892        // Ограничиваем историю последними 50 выборами
 893        if (this.userPreferences.contextualChoices.length > 50) {
 894            this.userPreferences.contextualChoices = this.userPreferences.contextualChoices.slice(-50);
 895        }
 896        
 897        this.saveUserPreferences();
 898        console.log('🧠 Система изучила предпочтения пользователя');
 899    }
 900    
 901    // === ОБНОВЛЕНИЕ СТАТУСА ===
 902    updateConnectionStatus(status, type) {
 903        document.getElementById('systemStatus').textContent = status;
 904        
 905        const statusDot = document.getElementById('statusDot');
 906        const colors = {
 907            online: '#4CAF50',
 908            offline: '#f44336',
 909            reconnecting: '#ff9800',
 910            error: '#f44336'
 911        };
 912        
 913        statusDot.style.backgroundColor = colors[type] || colors.offline;
 914    }
 915    
 916    updateBatteryIndicator() {
 917        if (!this.battery) return;
 918        
 919        const batteryElement = document.getElementById('batteryLevel');
 920        const level = Math.round(this.battery.level * 100);
 921        
 922        let icon = '🔋';
 923        if (level < 20) icon = '🪫';
 924        else if (level < 50) icon = '🔋';
 925        else icon = '🔋';
 926        
 927        if (this.battery.charging) icon = '⚡';
 928        
 929        batteryElement.textContent = icon;
 930        batteryElement.title = `Батарея: ${level}%`;
 931    }
 932    
 933    showWelcomeMessage() {
 934        const persona = this.currentPersona;
 935        const messages = {
 936            teacher: '👋 Добро пожаловать! Готов помочь с управлением классом.',
 937            student: '🎉 Привет! Давай изучать умные технологии вместе!',
 938            janitor: '🔧 Здравствуйте! Помогу следить за всеми системами.'
 939        };
 940        
 941        setTimeout(() => {
 942            this.showNotification(messages[persona] || messages.student, 'info');
 943        }, 1000);
 944    }
 945}
 946
 947// === ИНИЦИАЛИЗАЦИЯ ===
 948let smartInterface;
 949
 950document.addEventListener('DOMContentLoaded', function() {
 951    smartInterface = new SmartInterface();
 952});
 953
 954// === ДОПОЛНИТЕЛЬНЫЕ CSS АНИМАЦИИ ===
 955const additionalStyles = `
 956    @keyframes slideInRight {
 957        from { transform: translateX(100%); opacity: 0; }
 958        to { transform: translateX(0); opacity: 1; }
 959    }
 960    
 961    @keyframes slideOutRight {
 962        from { transform: translateX(0); opacity: 1; }
 963        to { transform: translateX(100%); opacity: 0; }
 964    }
 965    
 966    @keyframes fadeIn {
 967        from { opacity: 0; }
 968        to { opacity: 1; }
 969    }
 970    
 971    .modal-content {
 972        background: white;
 973        padding: 30px;
 974        border-radius: 15px;
 975        max-width: 500px;
 976        max-height: 80vh;
 977        overflow-y: auto;
 978        position: relative;
 979        color: #333;
 980    }
 981    
 982    .modal-close {
 983        position: absolute;
 984        top: 10px;
 985        right: 15px;
 986        background: none;
 987        border: none;
 988        font-size: 24px;
 989        cursor: pointer;
 990        color: #999;
 991    }
 992    
 993    .notification-content {
 994        display: flex;
 995        align-items: center;
 996        gap: 10px;
 997    }
 998    
 999    .notification-close {
1000        background: none;
1001        border: none;
1002        color: white;
1003        font-size: 18px;
1004        cursor: pointer;
1005        margin-left: auto;
1006    }
1007`;
1008
1009// Добавляем дополнительные стили
1010const styleSheet = document.createElement('style');
1011styleSheet.textContent = additionalStyles;
1012document.head.appendChild(styleSheet);

Занятие 3: “PWA и адаптивность” 📱

Длительность: 90 минут

Создание Progressive Web App (PWA):

 1// manifest.json
 2{
 3    "name": "ALEX - Умная система класса",
 4    "short_name": "ALEX",
 5    "description": "Интеллектуальная система управления классом",
 6    "start_url": "/",
 7    "display": "standalone",
 8    "theme_color": "#667eea",
 9    "background_color": "#667eea",
10    "orientation": "portrait-primary",
11    "icons": [
12        {
13            "src": "/icon-192.png",
14            "sizes": "192x192",
15            "type": "image/png"
16        },
17        {
18            "src": "/icon-512.png", 
19            "sizes": "512x512",
20            "type": "image/png"
21        }
22    ],
23    "categories": ["education", "productivity"],
24    "screenshots": [
25        {
26            "src": "/screenshot1.png",
27            "sizes": "1080x1920",
28            "type": "image/png"
29        }
30    ]
31}

Service Worker для офлайн работы:

 1// sw.js
 2const CACHE_NAME = 'alex-v1.0.0';
 3const urlsToCache = [
 4    '/',
 5    '/styles.css',
 6    '/script.js',
 7    '/manifest.json',
 8    '/icon-192.png',
 9    '/icon-512.png'
10];
11
12self.addEventListener('install', event => {
13    event.waitUntil(
14        caches.open(CACHE_NAME)
15            .then(cache => cache.addAll(urlsToCache))
16    );
17});
18
19self.addEventListener('fetch', event => {
20    event.respondWith(
21        caches.match(event.request)
22            .then(response => {
23                return response || fetch(event.request);
24            }
25        )
26    );
27});

Занятие 4: “Тестирование и оптимизация” 🧪

Длительность: 90 минут

A/B тестирование интерфейсов:

  • Дети создают 2 версии одного элемента
  • Тестируют на разных пользователях
  • Анализируют какой вариант лучше

Оптимизация производительности:

  • Минификация CSS/JS
  • Ленивая загрузка изображений
  • Кэширование данных

🎯 ИТОГИ СПРИНТА 18

Ключевые достижения:

Интерактивный интерфейс - живое взаимодействие с пользователем
Адаптивность - подстройка под разные типы пользователей
Мультимодальность - голос, жесты, клавиатура, касания
PWA - веб-приложение как нативное мобильное
Персонализация - система запоминает предпочтения
Доступность - интерфейс для всех категорий пользователей

Концептуальные прорывы:

  • UX-мышление - понимание потребностей пользователя
  • Эмпатический дизайн - технология с человеческим лицом
  • Адаптивные системы - интерфейс как живой организм
  • Мультимодальное взаимодействие - общение на языке пользователя

Технические навыки:

  • Продвинутый JavaScript и CSS3
  • Web APIs и PWA технологии
  • Accessibility и инклюзивный дизайн
  • UX/UI принципы и методологии

🚀 ПОДГОТОВКА К СПРИНТУ 19

Мостик к машинному обучению:

“Наш интерфейс умеет адаптироваться, но что если он научится предсказывать желания пользователей и самостоятельно предлагать оптимальные решения?”

Фундамент для AI:

  • ✅ Сбор данных о поведении пользователей
  • ✅ Контекстное понимание ситуаций
  • ✅ Персонализация и обучение на предпочтениях
  • ✅ Предиктивные предложения действий

Спринт 18 завершен! 📱
Дети создали по-настоящему живой интерфейс, который понимает пользователей и адаптируется под них!

Готов к анализу Спринта 19: “Введение в Machine Learning”! 🤖🧠