Mycc commited on
Commit
dcae22a
·
verified ·
1 Parent(s): af9d1d8

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +602 -19
index.html CHANGED
@@ -1,19 +1,602 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </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
+ <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>