Update index.html
Browse files- index.html +602 -19
index.html
CHANGED
|
@@ -1,19 +1,602 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
<title>Рулетка Отрядов Genshin Impact</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 9 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 11 |
+
<style>
|
| 12 |
+
body {
|
| 13 |
+
font-family: 'Inter', sans-serif;
|
| 14 |
+
transition: background-color 0.3s, color 0.3s;
|
| 15 |
+
}
|
| 16 |
+
.dark {
|
| 17 |
+
background-color: #1a202c;
|
| 18 |
+
color: #e2e8f0;
|
| 19 |
+
}
|
| 20 |
+
.light {
|
| 21 |
+
background-color: #FDF8F0;
|
| 22 |
+
color: #4A4A4A;
|
| 23 |
+
}
|
| 24 |
+
.roulette-char-card {
|
| 25 |
+
transition: transform 0.3s ease-out, opacity 0.3s ease-out;
|
| 26 |
+
transform: translateY(20px);
|
| 27 |
+
opacity: 0;
|
| 28 |
+
}
|
| 29 |
+
.roulette-char-card.show {
|
| 30 |
+
transform: translateY(0);
|
| 31 |
+
opacity: 1;
|
| 32 |
+
}
|
| 33 |
+
.loader {
|
| 34 |
+
width: 48px;
|
| 35 |
+
height: 48px;
|
| 36 |
+
border: 5px solid #FDF8F0;
|
| 37 |
+
border-bottom-color: #A089D0;
|
| 38 |
+
border-radius: 50%;
|
| 39 |
+
display: inline-block;
|
| 40 |
+
box-sizing: border-box;
|
| 41 |
+
animation: rotation 1s linear infinite;
|
| 42 |
+
}
|
| 43 |
+
@keyframes rotation {
|
| 44 |
+
0% { transform: rotate(0deg); }
|
| 45 |
+
100% { transform: rotate(360deg); }
|
| 46 |
+
}
|
| 47 |
+
.settings-panel {
|
| 48 |
+
max-height: 0;
|
| 49 |
+
overflow: hidden;
|
| 50 |
+
transition: max-height 0.3s ease-out;
|
| 51 |
+
}
|
| 52 |
+
.settings-panel.open {
|
| 53 |
+
max-height: 500px; /* Увеличено для вместимости содержимого */
|
| 54 |
+
}
|
| 55 |
+
/* Анимация для рулетки */
|
| 56 |
+
.roulette-spinning .roulette-char-card {
|
| 57 |
+
animation: spin-animation 0.1s linear infinite;
|
| 58 |
+
}
|
| 59 |
+
@keyframes spin-animation {
|
| 60 |
+
0% { transform: translateY(0) rotate(0deg); opacity: 1; }
|
| 61 |
+
50% { transform: translateY(-5px) rotate(2deg); opacity: 0.8; }
|
| 62 |
+
100% { transform: translateY(0) rotate(0deg); opacity: 1; }
|
| 63 |
+
}
|
| 64 |
+
.roulette-item-enter {
|
| 65 |
+
opacity: 0;
|
| 66 |
+
transform: scale(0.8);
|
| 67 |
+
}
|
| 68 |
+
.roulette-item-enter-active {
|
| 69 |
+
opacity: 1;
|
| 70 |
+
transform: scale(1);
|
| 71 |
+
transition: opacity 0.5s ease-out, transform 0.5s ease-out;
|
| 72 |
+
}
|
| 73 |
+
</style>
|
| 74 |
+
</head>
|
| 75 |
+
<body class="light">
|
| 76 |
+
<div id="app-container" class="container mx-auto px-4 py-8 md:py-12 max-w-4xl">
|
| 77 |
+
<header class="text-center mb-10">
|
| 78 |
+
<div class="flex justify-between items-center mb-4">
|
| 79 |
+
<button id="theme-toggle" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700">
|
| 80 |
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-800 dark:text-yellow-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
| 81 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
|
| 82 |
+
</svg>
|
| 83 |
+
</button>
|
| 84 |
+
<button id="settings-toggle" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700">
|
| 85 |
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
| 86 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
| 87 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
| 88 |
+
</svg>
|
| 89 |
+
</button>
|
| 90 |
+
</div>
|
| 91 |
+
<h1 class="text-4xl md:text-5xl font-bold text-[#4A4A4A] dark:text-[#e2e8f0] mb-2">Рулетка Отрядов Genshin Impact</h1>
|
| 92 |
+
<p class="text-lg text-[#6B6B6B] dark:text-[#a0aec0] max-w-2xl mx-auto">
|
| 93 |
+
Сгенерируйте случайный отряд из четырех персонажей Genshin Impact!
|
| 94 |
+
</p>
|
| 95 |
+
</header>
|
| 96 |
+
|
| 97 |
+
<main>
|
| 98 |
+
<div id="settings-panel" class="settings-panel bg-[#EFEAE4] dark:bg-gray-700 p-6 rounded-2xl shadow-lg mb-6">
|
| 99 |
+
<h3 class="text-xl font-bold mb-4 dark:text-white">Настройки рулетки</h3>
|
| 100 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 101 |
+
<div>
|
| 102 |
+
<label for="team-size" class="block text-sm font-medium mb-2 dark:text-gray-300">Количество персонажей в отряде</label>
|
| 103 |
+
<select id="team-size" class="w-full p-2 rounded border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white">
|
| 104 |
+
<option value="4">4</option>
|
| 105 |
+
<option value="3">3</option>
|
| 106 |
+
<option value="2">2</option>
|
| 107 |
+
<option value="1">1</option>
|
| 108 |
+
</select>
|
| 109 |
+
</div>
|
| 110 |
+
<div>
|
| 111 |
+
<label for="animation-speed" class="block text-sm font-medium mb-2 dark:text-gray-300">Скорость анимации</label>
|
| 112 |
+
<select id="animation-speed" class="w-full p-2 rounded border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white">
|
| 113 |
+
<option value="fast">Быстрая</option>
|
| 114 |
+
<option value="medium" selected>Средняя</option>
|
| 115 |
+
<option value="slow">Медленная</option>
|
| 116 |
+
</select>
|
| 117 |
+
</div>
|
| 118 |
+
</div>
|
| 119 |
+
</div>
|
| 120 |
+
|
| 121 |
+
<section id="roulette-section" class="mb-16 bg-[#EFEAE4] dark:bg-gray-700 p-6 rounded-2xl shadow-lg">
|
| 122 |
+
<h2 class="text-3xl font-bold text-center text-[#4A4A4A] dark:text-white mb-8">Сформировать отряд</h2>
|
| 123 |
+
<div class="flex justify-center mb-8">
|
| 124 |
+
<button id="spin-button" class="bg-[#DCA852] text-white font-bold py-4 px-10 rounded-lg text-xl shadow-md hover:bg-[#c3914a] transition-transform duration-200 ease-in-out hover:scale-105 focus:outline-none focus:ring-4 focus:ring-[#DCA852]/50">
|
| 125 |
+
Крутить отряд!
|
| 126 |
+
</button>
|
| 127 |
+
</div>
|
| 128 |
+
<div id="roulette-result" class="min-h-[180px] bg-[#FDF8F0] dark:bg-gray-800 rounded-xl p-4 flex flex-wrap justify-center items-center gap-4 transition-all duration-300">
|
| 129 |
+
<p id="roulette-placeholder" class="text-[#888] dark:text-gray-400">Ваш случайный отряд появится здесь.</p>
|
| 130 |
+
<div id="roulette-loader" class="loader hidden"></div>
|
| 131 |
+
</div>
|
| 132 |
+
</section>
|
| 133 |
+
|
| 134 |
+
<section id="history-section" class="bg-[#EFEAE4] dark:bg-gray-700 p-6 rounded-2xl shadow-lg">
|
| 135 |
+
<div class="flex justify-between items-center mb-8">
|
| 136 |
+
<h2 class="text-3xl font-bold text-[#4A4A4A] dark:text-white">История Отрядов</h2>
|
| 137 |
+
<button id="clear-history-button" class="bg-[#A089D0] text-white font-bold py-2 px-4 rounded-lg text-sm shadow-md hover:bg-[#8e7abf] transition-transform duration-200 ease-in-out hover:scale-105 focus:outline-none focus:ring-4 focus:ring-[#A089D0]/50 hidden">
|
| 138 |
+
Очистить
|
| 139 |
+
</button>
|
| 140 |
+
</div>
|
| 141 |
+
<div id="history-content" class="flex flex-col gap-6">
|
| 142 |
+
<p id="history-placeholder" class="text-center text-[#888] dark:text-gray-400">История ваших отрядов будет отображаться здесь.</p>
|
| 143 |
+
</div>
|
| 144 |
+
</section>
|
| 145 |
+
</main>
|
| 146 |
+
|
| 147 |
+
<footer class="text-center mt-16 pt-8 border-t border-[#EFEAE4] dark:border-gray-600">
|
| 148 |
+
<p class="text-sm text-[#888] dark:text-gray-400">
|
| 149 |
+
Это фанатский проект. Не аффилирован и не одобрен HoYoverse.
|
| 150 |
+
</p>
|
| 151 |
+
</footer>
|
| 152 |
+
</div>
|
| 153 |
+
|
| 154 |
+
<div id="confirm-modal" class="fixed inset-0 bg-black bg-opacity-70 z-50 flex justify-center items-center hidden p-4">
|
| 155 |
+
<div class="bg-[#FDF8F0] dark:bg-gray-800 rounded-2xl w-full max-w-sm shadow-2xl relative text-center p-6 transform transition-all duration-300 scale-95 opacity-0" id="confirm-modal-content">
|
| 156 |
+
<h3 class="text-2xl font-bold mb-4 text-[#4A4A4A] dark:text-white">Подтверждение</h3>
|
| 157 |
+
<p id="confirm-modal-message" class="text-lg text-[#6B6B6B] dark:text-gray-300 mb-6">Вы уверены, что хотите очистить историю?</p>
|
| 158 |
+
<div class="flex justify-around gap-4">
|
| 159 |
+
<button id="confirm-modal-cancel" class="bg-[#A089D0] text-white font-bold py-2 px-6 rounded-lg hover:bg-[#8e7abf]">Отмена</button>
|
| 160 |
+
<button id="confirm-modal-ok" class="bg-red-500 text-white font-bold py-2 px-6 rounded-lg hover:bg-red-600">Очистить</button>
|
| 161 |
+
</div>
|
| 162 |
+
</div>
|
| 163 |
+
</div>
|
| 164 |
+
|
| 165 |
+
<script>
|
| 166 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 167 |
+
// Локальные данные персонажей
|
| 168 |
+
const LOCAL_CHARACTERS = [
|
| 169 |
+
{ "name": "Альбедо", "icon": "https://game-cdn.appsample.com/gim/items/albedo.png" },
|
| 170 |
+
{ "name": "Аль-Хайтам", "icon": "https://game-cdn.appsample.com/gim/items/alhaitham.png" },
|
| 171 |
+
{ "name": "Алой", "icon": "https://game-cdn.appsample.com/gim/items/aloy.png" },
|
| 172 |
+
{ "name": "Эмбер", "icon": "https://game-cdn.appsample.com/gim/items/amber.png" },
|
| 173 |
+
{ "name": "Аратаки Итто", "icon": "https://game-cdn.appsample.com/gim/items/arataki_itto.png" },
|
| 174 |
+
{ "name": "Арлекино", "icon": "https://game-cdn.appsample.com/gim/items/arlecchino.png" },
|
| 175 |
+
{ "name": "Бай Чжу", "icon": "https://game-cdn.appsample.com/gim/items/baizhu.png" },
|
| 176 |
+
{ "name": "Барбара", "icon": "https://game-cdn.appsample.com/gim/items/barbara.png" },
|
| 177 |
+
{ "name": "Бэй Доу", "icon": "https://game-cdn.appsample.com/gim/items/beidou.png" },
|
| 178 |
+
{ "name": "Беннет", "icon": "https://game-cdn.appsample.com/gim/items/bennett.png" },
|
| 179 |
+
{ "name": "Кандакия", "icon": "https://game-cdn.appsample.com/gim/items/candace.png" },
|
| 180 |
+
{ "name": "Шарлотта", "icon": "https://game-cdn.appsample.com/gim/items/charlotte.png" },
|
| 181 |
+
{ "name": "Часца", "icon": "https://game-cdn.appsample.com/gim/items/chasca.png" },
|
| 182 |
+
{ "name": "Чиори", "icon": "https://game-cdn.appsample.com/gim/items/chiori.png" },
|
| 183 |
+
{ "name": "Чун Юнь", "icon": "https://game-cdn.appsample.com/gim/items/chongyun.png" },
|
| 184 |
+
{ "name": "Сителла", "icon": "https://game-cdn.appsample.com/gim/items/citlali.png" },
|
| 185 |
+
{ "name": "Клоринда", "icon": "https://game-cdn.appsample.com/gim/items/clorinde.png" },
|
| 186 |
+
{ "name": "Коллеи", "icon": "https://game-cdn.appsample.com/gim/items/collei.png" },
|
| 187 |
+
{ "name": "Сайно", "icon": "https://game-cdn.appsample.com/gim/items/cyno.png" },
|
| 188 |
+
{ "name": "Дэхья", "icon": "https://game-cdn.appsample.com/gim/items/dehya.png" },
|
| 189 |
+
{ "name": "Дилюк", "icon": "https://game-cdn.appsample.com/gim/items/diluc.png" },
|
| 190 |
+
{ "name": "Диона", "icon": "https://game-cdn.appsample.com/gim/items/diona.png" },
|
| 191 |
+
{ "name": "Дори", "icon": "https://game-cdn.appsample.com/gim/items/dori.png" },
|
| 192 |
+
{ "name": "Эмили", "icon": "https://game-cdn.appsample.com/gim/items/emilie.png" },
|
| 193 |
+
{ "name": "Эскофье", "icon": "https://game-cdn.appsample.com/gim/items/escoffier.png" },
|
| 194 |
+
{ "name": "Эола", "icon": "https://game-cdn.appsample.com/gim/items/eula.png" },
|
| 195 |
+
{ "name": "Фарузан", "icon": "https://game-cdn.appsample.com/gim/items/faruzan.png" },
|
| 196 |
+
{ "name": "Фишль", "icon": "https://game-cdn.appsample.com/gim/items/fischl.png" },
|
| 197 |
+
{ "name": "Фремине", "icon": "https://game-cdn.appsample.com/gim/items/freminet.png" },
|
| 198 |
+
{ "name": "Фурина", "icon": "https://game-cdn.appsample.com/gim/items/furina.png" },
|
| 199 |
+
{ "name": "Га Мин", "icon": "https://game-cdn.appsample.com/gim/items/gaming.png" },
|
| 200 |
+
{ "name": "Гань Юй", "icon": "https://game-cdn.appsample.com/gim/items/ganyu.png" },
|
| 201 |
+
{ "name": "Горо", "icon": "https://game-cdn.appsample.com/gim/items/gorou.png" },
|
| 202 |
+
{ "name": "Ху Тао", "icon": "https://game-cdn.appsample.com/gim/items/hu_tao.png" },
|
| 203 |
+
{ "name": "Ифу", "icon": "https://game-cdn.appsample.com/gim/items/ifa.png" },
|
| 204 |
+
{ "name": "Янсан", "icon": "https://game-cdn.appsample.com/gim/items/iansan.png" },
|
| 205 |
+
{ "name": "Джинн", "icon": "https://game-cdn.appsample.com/gim/items/jean.png" },
|
| 206 |
+
{ "name": "Кадзуха", "icon": "https://game-cdn.appsample.com/gim/items/kaedehara_kazuha.png" },
|
| 207 |
+
{ "name": "Кэйа", "icon": "https://game-cdn.appsample.com/gim/items/kaeya.png" },
|
| 208 |
+
{ "name": "Качина", "icon": "https://game-cdn.appsample.com/gim/items/kachina.png" },
|
| 209 |
+
{ "name": "Аяка", "icon": "https://game-cdn.appsample.com/gim/items/kamisato_ayaka.png" },
|
| 210 |
+
{ "name": "Аято", "icon": "https://game-cdn.appsample.com/gim/items/kamisato_ayato.png" },
|
| 211 |
+
{ "name": "Кавэх", "icon": "https://game-cdn.appsample.com/gim/items/kaveh.png" },
|
| 212 |
+
{ "name": "Кэ Цин", "icon": "https://game-cdn.appsample.com/gim/items/keqing.png" },
|
| 213 |
+
{ "name": "Кинич", "icon": "https://game-cdn.appsample.com/gim/items/kinich.png" },
|
| 214 |
+
{ "name": "Кирара", "icon": "https://game-cdn.appsample.com/gim/items/kirara.png" },
|
| 215 |
+
{ "name": "Кли", "icon": "https://game-cdn.appsample.com/gim/items/klee.png" },
|
| 216 |
+
{ "name": "Сара", "icon": "https://game-cdn.appsample.com/gim/items/kujou_sara.png" },
|
| 217 |
+
{ "name": "Шинобу", "icon": "https://game-cdn.appsample.com/gim/items/kuki_shinobu.png" },
|
| 218 |
+
{ "name": "Лань Янь", "icon": "https://game-cdn.appsample.com/gim/items/lan_yan.png" },
|
| 219 |
+
{ "name": "Лайла", "icon": "https://game-cdn.appsample.com/gim/items/layla.png" },
|
| 220 |
+
{ "name": "Лиза", "icon": "https://game-cdn.appsample.com/gim/items/lisa.png" },
|
| 221 |
+
{ "name": "Линнет", "icon": "https://game-cdn.appsample.com/gim/items/lynette.png" },
|
| 222 |
+
{ "name": "Лини", "icon": "https://game-cdn.appsample.com/gim/items/lyney.png" },
|
| 223 |
+
{ "name": "Мавуйка", "icon": "https://game-cdn.appsample.com/gim/items/mavuika.png" },
|
| 224 |
+
{ "name": "Мика", "icon": "https://game-cdn.appsample.com/gim/items/mika.png" },
|
| 225 |
+
{ "name": "Мона", "icon": "https://game-cdn.appsample.com/gim/items/mona.png" },
|
| 226 |
+
{ "name": "Муалани", "icon": "https://game-cdn.appsample.com/gim/items/mualani.png" },
|
| 227 |
+
{ "name": "Нахида", "icon": "https://game-cdn.appsample.com/gim/items/nahida.png" },
|
| 228 |
+
{ "name": "Навия", "icon": "https://game-cdn.appsample.com/gim/items/navia.png" },
|
| 229 |
+
{ "name": "Нёвиллет", "icon": "https://game-cdn.appsample.com/gim/items/neuvillette.png" },
|
| 230 |
+
{ "name": "Нилу", "icon": "https://game-cdn.appsample.com/gim/items/nilou.png" },
|
| 231 |
+
{ "name": "Нин Гуан", "icon": "https://game-cdn.appsample.com/gim/items/ningguang.png" },
|
| 232 |
+
{ "name": "Ноэлль", "icon": "https://game-cdn.appsample.com/gim/items/noelle.png" },
|
| 233 |
+
{ "name": "Оророн", "icon": "https://game-cdn.appsample.com/gim/items/ororon.png" },
|
| 234 |
+
{ "name": "Ци Ци", "icon": "https://game-cdn.appsample.com/gim/items/qiqi.png" },
|
| 235 |
+
{ "name": "Райдэн Сёгун", "icon": "https://game-cdn.appsample.com/gim/items/raiden_shogun.png" },
|
| 236 |
+
{ "name": "Рэйзор", "icon": "https://game-cdn.appsample.com/gim/items/razor.png" },
|
| 237 |
+
{ "name": "Розария", "icon": "https://game-cdn.appsample.com/gim/items/rosaria.png" },
|
| 238 |
+
{ "name": "Сангономия Кокоми", "icon": "https://game-cdn.appsample.com/gim/items/sangonomiya_kokomi.png" },
|
| 239 |
+
{ "name": "Саю", "icon": "https://game-cdn.appsample.com/gim/items/sayu.png" },
|
| 240 |
+
{ "name": "Сетусы", "icon": "https://game-cdn.appsample.com/gim/items/sethos.png" },
|
| 241 |
+
{ "name": "Шэнь Хэ", "icon": "https://game-cdn.appsample.com/gim/items/shenhe.png" },
|
| 242 |
+
{ "name": "Сиканоин Хэйдзо", "icon": "https://game-cdn.appsample.com/gim/items/shikanoin_heizou.png" },
|
| 243 |
+
{ "name": "Сигевинн", "icon": "https://game-cdn.appsample.com/gim/items/sigewinne.png" },
|
| 244 |
+
{ "name": "Сахароза", "icon": "https://game-cdn.appsample.com/gim/items/sucrose.png" },
|
| 245 |
+
{ "name": "Тарталья", "icon": "https://game-cdn.appsample.com/gim/items/tartaglia.png" },
|
| 246 |
+
{ "name": "Тома", "icon": "https://game-cdn.appsample.com/gim/items/thoma.png" },
|
| 247 |
+
{ "name": "Тигнари", "icon": "https://game-cdn.appsample.com/gim/items/tighnari.png" },
|
| 248 |
+
{ "name": "Вареса", "icon": "https://game-cdn.appsample.com/gim/items/varesa.png" },
|
| 249 |
+
{ "name": "Венти", "icon": "https://game-cdn.appsample.com/gim/items/venti.png" },
|
| 250 |
+
{ "name": "Странник", "icon": "https://game-cdn.appsample.com/gim/items/wanderer.png" },
|
| 251 |
+
{ "name": "Райотсли", "icon": "https://game-cdn.appsample.com/gim/items/wriothesley.png" },
|
| 252 |
+
{ "name": "Сяо", "icon": "https://game-cdn.appsample.com/gim/items/xiao.png" },
|
| 253 |
+
{ "name": "Сян Линь", "icon": "https://game-cdn.appsample.com/gim/items/xiangling.png" },
|
| 254 |
+
{ "name": "Сянь Юнь", "icon": "https://game-cdn.appsample.com/gim/items/xianyun.png" },
|
| 255 |
+
{ "name": "Силонен", "icon": "https://game-cdn.appsample.com/gim/items/xilonen.png" },
|
| 256 |
+
{ "name": "Син Цю", "icon": "https://game-cdn.appsample.com/gim/items/xingqiu.png" },
|
| 257 |
+
{ "name": "Синь Янь", "icon": "https://game-cdn.appsample.com/gim/items/xinyan.png" },
|
| 258 |
+
{ "name": "Яэ Мико", "icon": "https://game-cdn.appsample.com/gim/items/yae_miko.png" },
|
| 259 |
+
{ "name": "Янь Фэй", "icon": "https://game-cdn.appsample.com/gim/items/yanfei.png" },
|
| 260 |
+
{ "name": "Яо Яо", "icon": "https://game-cdn.appsample.com/gim/items/yaoyao.png" },
|
| 261 |
+
{ "name": "Е Лань", "icon": "https://game-cdn.appsample.com/gim/items/yelan.png" },
|
| 262 |
+
{ "name": "Ёимия", "icon": "https://game-cdn.appsample.com/gim/items/yoimiya.png" },
|
| 263 |
+
{ "name": "Юнь Цзинь", "icon": "https://game-cdn.appsample.com/gim/items/yun_jin.png" },
|
| 264 |
+
{ "name": "Юмемидзуки Мидзуки", "icon": "https://game-cdn.appsample.com/gim/items/yumemizuki_mizuki.png" },
|
| 265 |
+
{ "name": "Чжун Ли", "icon": "https://game-cdn.appsample.com/gim/items/zhongli.png" }
|
| 266 |
+
];
|
| 267 |
+
|
| 268 |
+
// Генерация или получение UID
|
| 269 |
+
function getOrCreateUID() {
|
| 270 |
+
let uid = localStorage.getItem('genshinRouletteUID');
|
| 271 |
+
if (!uid) {
|
| 272 |
+
uid = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
| 273 |
+
localStorage.setItem('genshinRouletteUID', uid);
|
| 274 |
+
}
|
| 275 |
+
return uid;
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
const currentUserUID = getOrCreateUID();
|
| 279 |
+
|
| 280 |
+
// Конфигурация
|
| 281 |
+
const config = {
|
| 282 |
+
teamSize: parseInt(localStorage.getItem(`${currentUserUID}_teamSize`)) || 4,
|
| 283 |
+
animationSpeed: localStorage.getItem(`${currentUserUID}_animationSpeed`) || 'medium',
|
| 284 |
+
darkMode: localStorage.getItem('theme') === 'dark'
|
| 285 |
+
};
|
| 286 |
+
|
| 287 |
+
// Состояние приложения
|
| 288 |
+
let allCharacters = [...LOCAL_CHARACTERS];
|
| 289 |
+
let historyTeams = JSON.parse(localStorage.getItem(`${currentUserUID}_genshinTeamsHistory`)) || [];
|
| 290 |
+
let isSpinning = false;
|
| 291 |
+
|
| 292 |
+
// Элементы DOM
|
| 293 |
+
const spinButton = document.getElementById('spin-button');
|
| 294 |
+
const rouletteResult = document.getElementById('roulette-result');
|
| 295 |
+
const roulettePlaceholder = document.getElementById('roulette-placeholder');
|
| 296 |
+
const rouletteLoader = document.getElementById('roulette-loader');
|
| 297 |
+
const historyContent = document.getElementById('history-content');
|
| 298 |
+
const historyPlaceholder = document.getElementById('history-placeholder');
|
| 299 |
+
const clearHistoryButton = document.getElementById('clear-history-button');
|
| 300 |
+
const themeToggle = document.getElementById('theme-toggle');
|
| 301 |
+
const settingsToggle = document.getElementById('settings-toggle');
|
| 302 |
+
const settingsPanel = document.getElementById('settings-panel');
|
| 303 |
+
const teamSizeSelect = document.getElementById('team-size');
|
| 304 |
+
const animationSpeedSelect = document.getElementById('animation-speed');
|
| 305 |
+
const confirmModal = document.getElementById('confirm-modal');
|
| 306 |
+
const confirmModalContent = document.getElementById('confirm-modal-content');
|
| 307 |
+
const confirmModalMessage = document.getElementById('confirm-modal-message');
|
| 308 |
+
const confirmModalCancel = document.getElementById('confirm-modal-cancel');
|
| 309 |
+
const confirmModalOk = document.getElementById('confirm-modal-ok');
|
| 310 |
+
|
| 311 |
+
// Инициализация темы
|
| 312 |
+
function initTheme() {
|
| 313 |
+
if (config.darkMode) {
|
| 314 |
+
document.body.classList.remove('light');
|
| 315 |
+
document.body.classList.add('dark');
|
| 316 |
+
} else {
|
| 317 |
+
document.body.classList.remove('dark');
|
| 318 |
+
document.body.classList.add('light');
|
| 319 |
+
}
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
// Переключение темы
|
| 323 |
+
function toggleTheme() {
|
| 324 |
+
config.darkMode = !config.darkMode;
|
| 325 |
+
initTheme();
|
| 326 |
+
localStorage.setItem('theme', config.darkMode ? 'dark' : 'light');
|
| 327 |
+
}
|
| 328 |
+
|
| 329 |
+
// Переключение настроек
|
| 330 |
+
function toggleSettings() {
|
| 331 |
+
settingsPanel.classList.toggle('open');
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
// Попытка загрузки персонажей из API
|
| 335 |
+
async function tryFetchCharacters() {
|
| 336 |
+
try {
|
| 337 |
+
// Используем setTimeout для имитации задержки загрузки, чтобы показать лоадер
|
| 338 |
+
setTimeout(async () => {
|
| 339 |
+
rouletteLoader.classList.remove('hidden');
|
| 340 |
+
roulettePlaceholder.classList.add('hidden');
|
| 341 |
+
spinButton.disabled = true;
|
| 342 |
+
|
| 343 |
+
// Имитация загрузки из API (реальный CORS запрос без п��окси не пройдет)
|
| 344 |
+
// В реальном приложении этот fetch будет работать, если API разрешит CORS
|
| 345 |
+
// const response = await fetch('https://akasha.cv/api/filters/characters/?type=profile');
|
| 346 |
+
// if (!response.ok) throw new Error('Ошибка загрузки данных');
|
| 347 |
+
// const data = await response.json();
|
| 348 |
+
// if (data && data.data && data.data[0] && data.data[0].options) {
|
| 349 |
+
// allCharacters = data.data[0].options.map(char => ({
|
| 350 |
+
// name: char.name,
|
| 351 |
+
// icon: char.icon
|
| 352 |
+
// }));
|
| 353 |
+
// console.log('Загружено персонажей из API:', allCharacters.length);
|
| 354 |
+
// } else {
|
| 355 |
+
// throw new Error('Неверный формат данных');
|
| 356 |
+
// }
|
| 357 |
+
|
| 358 |
+
// В текущей реализации всегда используем локальные данные, т.к. CORS не позволяет
|
| 359 |
+
allCharacters = [...LOCAL_CHARACTERS];
|
| 360 |
+
console.log('Используем локальные данные персонажей:', allCharacters.length);
|
| 361 |
+
|
| 362 |
+
rouletteLoader.classList.add('hidden');
|
| 363 |
+
spinButton.disabled = false;
|
| 364 |
+
roulettePlaceholder.classList.remove('hidden'); // Показываем плейсхолдер после загрузки
|
| 365 |
+
}, 500); // Небольшая задержка для имитации загрузки
|
| 366 |
+
} catch (error) {
|
| 367 |
+
console.error('Ошибка загрузки персонажей:', error);
|
| 368 |
+
allCharacters = [...LOCAL_CHARACTERS];
|
| 369 |
+
console.log('Используем локальные данные персонажей (из-за ошибки загрузки):', allCharacters.length);
|
| 370 |
+
rouletteLoader.classList.add('hidden');
|
| 371 |
+
spinButton.disabled = false;
|
| 372 |
+
roulettePlaceholder.classList.remove('hidden'); // Показываем плейсхолдер после загрузки
|
| 373 |
+
}
|
| 374 |
+
}
|
| 375 |
+
|
| 376 |
+
// Создание карточки персонажа
|
| 377 |
+
function createCharacterCard(character, isRoulette = false) {
|
| 378 |
+
const card = document.createElement('div');
|
| 379 |
+
card.className = `flex flex-col items-center p-2 rounded-lg bg-white dark:bg-gray-700 shadow-sm text-center flex-shrink-0 w-[100px] sm:w-[120px] md:w-[130px] lg:w-[140px]`;
|
| 380 |
+
if (isRoulette) card.classList.add('roulette-char-card');
|
| 381 |
+
|
| 382 |
+
card.innerHTML = `
|
| 383 |
+
<img src="${character.icon}" alt="${character.name}"
|
| 384 |
+
class="w-16 h-16 sm:w-20 sm:h-20 rounded-full mb-1 object-cover border-2 border-[#DCA852]"
|
| 385 |
+
onerror="this.onerror=null;this.src='https://placehold.co/80x80/EFEAE4/4A4A4A?text=N/A'">
|
| 386 |
+
<p class="font-medium text-sm truncate w-full dark:text-white">${character.name}</p>
|
| 387 |
+
`;
|
| 388 |
+
return card;
|
| 389 |
+
}
|
| 390 |
+
|
| 391 |
+
// Отображение результата рулетки с плавной анимацией
|
| 392 |
+
function renderRouletteResult(team) {
|
| 393 |
+
rouletteResult.innerHTML = '';
|
| 394 |
+
roulettePlaceholder.classList.add('hidden');
|
| 395 |
+
|
| 396 |
+
team.forEach((char, index) => {
|
| 397 |
+
const card = createCharacterCard(char, true);
|
| 398 |
+
rouletteResult.appendChild(card);
|
| 399 |
+
|
| 400 |
+
// Анимация появления с задержкой
|
| 401 |
+
setTimeout(() => {
|
| 402 |
+
card.classList.add('show');
|
| 403 |
+
}, index * 150); // Уменьшил задержку для более быстрого появления
|
| 404 |
+
});
|
| 405 |
+
}
|
| 406 |
+
|
| 407 |
+
// Отображение истории
|
| 408 |
+
function renderHistory() {
|
| 409 |
+
historyContent.innerHTML = '';
|
| 410 |
+
|
| 411 |
+
if (historyTeams.length === 0) {
|
| 412 |
+
historyPlaceholder.classList.remove('hidden');
|
| 413 |
+
clearHistoryButton.classList.add('hidden');
|
| 414 |
+
} else {
|
| 415 |
+
historyPlaceholder.classList.add('hidden');
|
| 416 |
+
clearHistoryButton.classList.remove('hidden');
|
| 417 |
+
|
| 418 |
+
historyTeams.forEach((team, index) => {
|
| 419 |
+
const teamElement = document.createElement('div');
|
| 420 |
+
teamElement.className = 'bg-[#FDF8F0] dark:bg-gray-800 rounded-xl p-4 shadow-sm border border-[#EFEAE4] dark:border-gray-600';
|
| 421 |
+
|
| 422 |
+
const title = document.createElement('h3');
|
| 423 |
+
title.className = 'text-lg font-semibold mb-3 dark:text-white';
|
| 424 |
+
title.textContent = `Отряд ${historyTeams.length - index} (${new Date(team.date).toLocaleString()})`;
|
| 425 |
+
teamElement.appendChild(title);
|
| 426 |
+
|
| 427 |
+
const charsContainer = document.createElement('div');
|
| 428 |
+
charsContainer.className = 'flex flex-wrap justify-center gap-3';
|
| 429 |
+
|
| 430 |
+
team.characters.forEach(char => {
|
| 431 |
+
charsContainer.appendChild(createCharacterCard(char));
|
| 432 |
+
});
|
| 433 |
+
|
| 434 |
+
teamElement.appendChild(charsContainer);
|
| 435 |
+
historyContent.appendChild(teamElement);
|
| 436 |
+
});
|
| 437 |
+
}
|
| 438 |
+
}
|
| 439 |
+
|
| 440 |
+
// Сохранение отряда в историю
|
| 441 |
+
function saveTeamToHistory(team) {
|
| 442 |
+
historyTeams.unshift({
|
| 443 |
+
date: new Date().toISOString(),
|
| 444 |
+
characters: team
|
| 445 |
+
});
|
| 446 |
+
|
| 447 |
+
// Ограничиваем историю последними 10 отрядами
|
| 448 |
+
if (historyTeams.length > 10) {
|
| 449 |
+
historyTeams = historyTeams.slice(0, 10);
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
localStorage.setItem(`${currentUserUID}_genshinTeamsHistory`, JSON.stringify(historyTeams));
|
| 453 |
+
renderHistory();
|
| 454 |
+
}
|
| 455 |
+
|
| 456 |
+
// Очистка истории
|
| 457 |
+
function clearHistory() {
|
| 458 |
+
historyTeams = [];
|
| 459 |
+
localStorage.removeItem(`${currentUserUID}_genshinTeamsHistory`);
|
| 460 |
+
renderHistory();
|
| 461 |
+
hideConfirmModal();
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
// Запуск рулетки
|
| 465 |
+
async function spinRoulette() {
|
| 466 |
+
if (isSpinning || allCharacters.length < config.teamSize) return;
|
| 467 |
+
|
| 468 |
+
isSpinning = true;
|
| 469 |
+
spinButton.disabled = true;
|
| 470 |
+
spinButton.textContent = 'Крутим...';
|
| 471 |
+
rouletteResult.innerHTML = ''; // Очищаем поле результата
|
| 472 |
+
roulettePlaceholder.classList.add('hidden');
|
| 473 |
+
rouletteLoader.classList.remove('hidden');
|
| 474 |
+
rouletteResult.classList.add('roulette-spinning'); // Добавляем класс для анимации вращения
|
| 475 |
+
|
| 476 |
+
const teamSize = parseInt(teamSizeSelect.value);
|
| 477 |
+
let availableChars = [...allCharacters];
|
| 478 |
+
const selectedTeam = [];
|
| 479 |
+
|
| 480 |
+
let spinDurationBase; // Общая базовая продолжительность анимации
|
| 481 |
+
switch (animationSpeedSelect.value) {
|
| 482 |
+
case 'fast': spinDurationBase = 1000; break; // 1 секунда
|
| 483 |
+
case 'slow': spinDurationBase = 3000; break; // 3 секунды
|
| 484 |
+
default: spinDurationBase = 2000; // 2 секунды
|
| 485 |
+
}
|
| 486 |
+
|
| 487 |
+
const delayBetweenChars = spinDurationBase / (teamSize * 2); // Задержка между появлением каждого символа
|
| 488 |
+
const totalSpinTimePerChar = spinDurationBase / teamSize; // Общее время "кручения" для каждого символа
|
| 489 |
+
|
| 490 |
+
for (let i = 0; i < teamSize; i++) {
|
| 491 |
+
// Анимируем "прокрутку" перед выбором каждого персонажа
|
| 492 |
+
const tempRouletteChars = [];
|
| 493 |
+
const numTempChars = 10; // Количество временных символов для прокрутки
|
| 494 |
+
for (let j = 0; j < numTempChars; j++) {
|
| 495 |
+
const randomChar = availableChars[Math.floor(Math.random() * availableChars.length)];
|
| 496 |
+
tempRouletteChars.push(randomChar);
|
| 497 |
+
}
|
| 498 |
+
|
| 499 |
+
// Показываем временные символы с быстрой сменой
|
| 500 |
+
for (let k = 0; k < numTempChars; k++) {
|
| 501 |
+
setTimeout(() => {
|
| 502 |
+
rouletteResult.innerHTML = '';
|
| 503 |
+
const tempCard = createCharacterCard(tempRouletteChars[k], true);
|
| 504 |
+
rouletteResult.appendChild(tempCard);
|
| 505 |
+
tempCard.classList.add('roulette-item-enter-active'); // Применяем анимацию появления
|
| 506 |
+
}, k * (totalSpinTimePerChar / numTempChars)); // Быстрая смена временных символов
|
| 507 |
+
}
|
| 508 |
+
|
| 509 |
+
// Выбираем окончательного персонажа после анимации прокрутки
|
| 510 |
+
await new Promise(resolve => setTimeout(resolve, totalSpinTimePerChar)); // Задержка для анимации прокрутки
|
| 511 |
+
|
| 512 |
+
const selectedIndex = Math.floor(Math.random() * availableChars.length);
|
| 513 |
+
const selectedChar = availableChars.splice(selectedIndex, 1)[0];
|
| 514 |
+
selectedTeam.push(selectedChar);
|
| 515 |
+
|
| 516 |
+
// Очищаем и добавляем выбранного персонажа
|
| 517 |
+
rouletteResult.innerHTML = ''; // Очищаем временные символы
|
| 518 |
+
const finalCard = createCharacterCard(selectedChar, true);
|
| 519 |
+
rouletteResult.appendChild(finalCard);
|
| 520 |
+
finalCard.classList.add('show'); // Показываем выбранного персонажа с плавной анимацией
|
| 521 |
+
|
| 522 |
+
await new Promise(resolve => setTimeout(resolve, delayBetweenChars)); // Задержка перед следующим персонажем
|
| 523 |
+
}
|
| 524 |
+
|
| 525 |
+
// Завершение рулетки
|
| 526 |
+
rouletteLoader.classList.add('hidden');
|
| 527 |
+
spinButton.disabled = false;
|
| 528 |
+
spinButton.textContent = 'Крутить ещё!';
|
| 529 |
+
isSpinning = false;
|
| 530 |
+
rouletteResult.classList.remove('roulette-spinning'); // Удаляем класс анимации вращения
|
| 531 |
+
|
| 532 |
+
// Сохраняем отряд в историю
|
| 533 |
+
saveTeamToHistory(selectedTeam);
|
| 534 |
+
}
|
| 535 |
+
|
| 536 |
+
// Модальное окно подтверждения
|
| 537 |
+
function showConfirmModal(message, callback) {
|
| 538 |
+
confirmModalMessage.textContent = message;
|
| 539 |
+
confirmModal.classList.remove('hidden');
|
| 540 |
+
setTimeout(() => {
|
| 541 |
+
confirmModalContent.classList.remove('scale-95', 'opacity-0');
|
| 542 |
+
confirmModalContent.classList.add('scale-100', 'opacity-100');
|
| 543 |
+
}, 10);
|
| 544 |
+
|
| 545 |
+
const handleConfirm = () => {
|
| 546 |
+
callback();
|
| 547 |
+
confirmModalOk.removeEventListener('click', handleConfirm);
|
| 548 |
+
confirmModalCancel.removeEventListener('click', handleCancel);
|
| 549 |
+
};
|
| 550 |
+
|
| 551 |
+
const handleCancel = () => {
|
| 552 |
+
hideConfirmModal();
|
| 553 |
+
confirmModalOk.removeEventListener('click', handleConfirm);
|
| 554 |
+
confirmModalCancel.removeEventListener('click', handleCancel);
|
| 555 |
+
};
|
| 556 |
+
|
| 557 |
+
confirmModalOk.addEventListener('click', handleConfirm);
|
| 558 |
+
confirmModalCancel.addEventListener('click', handleCancel);
|
| 559 |
+
}
|
| 560 |
+
|
| 561 |
+
function hideConfirmModal() {
|
| 562 |
+
confirmModalContent.classList.add('scale-95', 'opacity-0');
|
| 563 |
+
confirmModalContent.classList.remove('scale-100', 'opacity-100');
|
| 564 |
+
setTimeout(() => {
|
| 565 |
+
confirmModal.classList.add('hidden');
|
| 566 |
+
}, 300);
|
| 567 |
+
}
|
| 568 |
+
|
| 569 |
+
// Обработчики событий
|
| 570 |
+
spinButton.addEventListener('click', spinRoulette);
|
| 571 |
+
themeToggle.addEventListener('click', toggleTheme);
|
| 572 |
+
settingsToggle.addEventListener('click', toggleSettings);
|
| 573 |
+
clearHistoryButton.addEventListener('click', () => {
|
| 574 |
+
showConfirmModal('Вы уверены, что хотите очистить всю историю отрядов?', clearHistory);
|
| 575 |
+
});
|
| 576 |
+
confirmModal.addEventListener('click', (e) => {
|
| 577 |
+
if (e.target === confirmModal) hideConfirmModal();
|
| 578 |
+
});
|
| 579 |
+
|
| 580 |
+
// Сохранение настроек при изменении
|
| 581 |
+
teamSizeSelect.addEventListener('change', (e) => {
|
| 582 |
+
config.teamSize = parseInt(e.target.value);
|
| 583 |
+
localStorage.setItem(`${currentUserUID}_teamSize`, config.teamSize);
|
| 584 |
+
});
|
| 585 |
+
|
| 586 |
+
animationSpeedSelect.addEventListener('change', (e) => {
|
| 587 |
+
config.animationSpeed = e.target.value;
|
| 588 |
+
localStorage.setItem(`${currentUserUID}_animationSpeed`, config.animationSpeed);
|
| 589 |
+
});
|
| 590 |
+
|
| 591 |
+
// Установка сохраненных настроек при загрузке
|
| 592 |
+
teamSizeSelect.value = config.teamSize;
|
| 593 |
+
animationSpeedSelect.value = config.animationSpeed;
|
| 594 |
+
|
| 595 |
+
// Инициализация приложения
|
| 596 |
+
initTheme();
|
| 597 |
+
tryFetchCharacters(); // Пытаемся загрузить данные с API, но используем локальные в случае ошибки
|
| 598 |
+
renderHistory();
|
| 599 |
+
});
|
| 600 |
+
</script>
|
| 601 |
+
</body>
|
| 602 |
+
</html>
|