Aleksmorshen commited on
Commit
f6f9e61
·
verified ·
1 Parent(s): 5f69ca1

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +346 -328
index.html CHANGED
@@ -4,6 +4,10 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
  <title>Arctic Mineral Water - 3D Презентация</title>
 
 
 
 
7
  <style>
8
  html, body {
9
  overflow: hidden;
@@ -11,409 +15,423 @@
11
  height: 100%;
12
  margin: 0;
13
  padding: 0;
14
- font-family: Arial, sans-serif;
15
- background-color: #000; /* Фон на случай долгой загрузки */
 
16
  }
17
 
18
  #renderCanvas {
19
  width: 100%;
20
  height: 100%;
21
- touch-action: none; /* Важно для Babylon.js на мобильных */
22
  }
23
 
24
- /* Стили для информационных блоков */
25
- .info-overlay {
26
  position: absolute;
27
- top: 15%;
28
- left: 50%;
29
- transform: translateX(-50%);
 
 
30
  color: white;
31
- background-color: rgba(0, 20, 50, 0.7);
32
- padding: 15px 25px;
33
- border-radius: 10px;
34
- text-align: center;
35
- max-width: 80%;
36
- box-shadow: 0 0 15px rgba(100, 150, 255, 0.5);
 
 
37
  opacity: 0;
38
- transition: opacity 0.5s ease-in-out;
39
- pointer-events: none; /* Чтобы не мешать взаимодействию с canvas */
40
  }
41
 
42
- .info-overlay.visible {
43
- opacity: 1;
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
 
46
- .info-overlay h2 {
47
- margin-top: 0;
48
- margin-bottom: 10px;
49
- font-size: 1.5em;
50
- color: #cceeff;
 
 
 
 
 
51
  }
52
 
53
- .info-overlay p {
54
- margin-bottom: 0;
55
- font-size: 1em;
56
  }
 
 
 
57
 
58
- /* Стили для кнопок навигации */
59
- #navigation {
60
  position: absolute;
61
- bottom: 20px;
62
  left: 50%;
63
  transform: translateX(-50%);
64
- display: flex;
65
- gap: 15px;
66
- z-index: 10;
 
 
 
 
 
 
 
 
 
67
  }
68
 
69
- #navigation button {
70
- padding: 12px 20px;
71
- font-size: 1em;
72
- color: white;
73
- background-color: rgba(0, 50, 100, 0.8);
74
- border: 1px solid #cceeff;
75
- border-radius: 25px;
76
- cursor: pointer;
77
- transition: background-color 0.3s ease, transform 0.1s ease;
78
- box-shadow: 0 0 10px rgba(100, 150, 255, 0.4);
79
  }
80
 
81
- #navigation button:disabled {
82
- background-color: rgba(50, 50, 50, 0.6);
83
- color: #aaa;
84
- cursor: not-allowed;
85
- border-color: #777;
86
- }
 
 
 
 
 
 
 
87
 
88
- #navigation button:not(:disabled):active {
89
- transform: scale(0.95);
 
 
 
 
 
 
 
 
90
  }
91
 
92
- #loadingScreen {
93
- position: absolute;
94
- top: 0;
95
- left: 0;
96
- width: 100%;
97
- height: 100%;
98
- background-color: #05101f;
99
- color: white;
100
- display: flex;
101
- justify-content: center;
102
- align-items: center;
103
- font-size: 1.5em;
104
- z-index: 100;
105
- transition: opacity 1s ease-out;
106
- }
107
  </style>
108
- <!-- Подключение Babylon.js -->
109
- <script src="https://cdn.babylonjs.com/babylon.js"></script>
110
- <!-- Опционально: для загрузки моделей GLTF/GLB -->
111
- <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
112
- <!-- Опционально: для удобных материалов -->
113
- <script src="https://cdn.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
114
-
115
  </head>
116
  <body>
117
 
118
- <div id="loadingScreen">Загрузка арктической среды...</div>
 
 
119
 
120
  <canvas id="renderCanvas"></canvas>
121
 
122
- <!-- Информационные блоки (скрыты по умолчанию) -->
123
- <div id="info-welcome" class="info-overlay">
124
- <h2>Arctic</h2>
125
- <p>Чистота северных ледников в каждой капле.</p>
126
- </div>
127
- <div id="info-source" class="info-overlay">
128
- <h2>Источник</h2>
129
- <p>Добывается из реликтового подземного озера под вечной мерзлотой.</p>
130
- </div>
131
- <div id="info-purity" class="info-overlay">
132
- <h2>Кристальная Чистота</h2>
133
- <p>Многоступенчатая природная фильтрация сквозь горные породы.</p>
134
- </div>
135
- <div id="info-bottle" class="info-overlay">
136
- <h2>Уникальный Дизайн</h2>
137
- <p>Бутылка, вдохновленная формой айсберга.</p>
138
  </div>
139
 
140
- <!-- Кнопки навигации -->
141
- <div id="navigation">
142
- <button id="prevBtn" disabled>Назад</button>
143
- <button id="nextBtn">Далее</button>
144
  </div>
145
 
146
  <script>
147
- const canvas = document.getElementById('renderCanvas');
148
- const engine = new BABYLON.Engine(canvas, true, { stencil: true, preserveDrawingBuffer: true }, true);
149
  const loadingScreen = document.getElementById('loadingScreen');
150
- const infoOverlays = {
151
- welcome: document.getElementById('info-welcome'),
152
- source: document.getElementById('info-source'),
153
- purity: document.getElementById('info-purity'),
154
- bottle: document.getElementById('info-bottle')
155
- };
156
- const prevBtn = document.getElementById('prevBtn');
157
- const nextBtn = document.getElementById('nextBtn');
158
-
159
- let currentStage = 0;
160
- let scene = null;
161
- let camera = null;
162
- const animationDuration = 1.5; // Длительность анимации перехода (в секундах)
163
-
164
- // Скрыть лоадер, когда сцена готова
165
- engine.loadingScreen = {
166
- displayLoadingUI: function() {
167
- loadingScreen.style.opacity = '1';
168
  },
169
- hideLoadingUI: function() {
170
- loadingScreen.style.opacity = '0';
171
- // Скрыть после завершения анимации исчезновения
172
- setTimeout(() => { loadingScreen.style.display = 'none'; }, 1000);
173
  },
174
- loadingUIBackgroundColor: "#05101f",
175
- loadingUIText: "Загрузка арктической среды..."
 
 
 
 
 
 
176
  };
177
 
178
- engine.displayLoadingUI(); // Показать лоадер сразу
 
 
 
 
 
179
 
 
180
  function createScene() {
181
  scene = new BABYLON.Scene(engine);
182
- scene.clearColor = new BABYLON.Color3(0.1, 0.2, 0.35); // Темно-синий фон
183
 
184
  // --- Камера ---
185
- // Используем UniversalCamera для лучшего контроля над анимацией
 
186
  camera = new BABYLON.UniversalCamera("camera", new BABYLON.Vector3(0, 5, -20), scene);
187
- camera.setTarget(new BABYLON.Vector3(0, 2, 0));
188
- // camera.attachControl(canvas, true); // Отключаем управление камерой по умолчанию
 
 
189
 
190
- // --- Освещение ---
191
- const light = new BABYLON.HemisphericLight("hemiLight", new BABYLON.Vector3(0, 1, 0), scene);
192
  light.intensity = 0.8;
193
  light.groundColor = new BABYLON.Color3(0.5, 0.7, 1); // Холодный цвет снизу
194
- light.specular = BABYLON.Color3.Black(); // Убираем блики от этого света
195
-
196
- const dirLight = new BABYLON.DirectionalLight("dirLight", new BABYLON.Vector3(-0.5, -1, -0.8), scene);
197
- dirLight.position = new BABYLON.Vector3(20, 40, 30);
198
- dirLight.intensity = 0.7;
199
- dirLight.shadowMinZ = 1;
200
- dirLight.shadowMaxZ = 150;
201
 
202
- // --- Тень ---
203
- const shadowGenerator = new BABYLON.ShadowGenerator(1024, dirLight);
204
- shadowGenerator.useBlurExponentialShadowMap = true;
205
- shadowGenerator.blurKernel = 32;
206
- shadowGenerator.darkness = 0.5;
207
 
208
- // --- Skybox (Арктическое небо) ---
209
  const skybox = BABYLON.MeshBuilder.CreateBox("skyBox", { size: 1000.0 }, scene);
210
- const skyboxMaterial = new BABYLON.StandardMaterial("skyBoxMat", scene);
211
- skyboxMaterial.backFaceCulling = false;
212
- // Используем кубическую текстуру. Замените пути на реальные URL текстур неба!
213
- // Эти текстуры взяты из примеров BabylonJS Playground
214
- skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("https://playground.babylonjs.com/textures/skybox_ice", scene);
215
- // Старые версии использовали:
216
- // skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("textures/TropicalSunnyDay", scene); // Заменить на арктическую!
 
217
  skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
218
- skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
219
  skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
 
220
  skybox.material = skyboxMaterial;
221
- skybox.infiniteDistance = true; // Фиксируем скайбокс
222
-
223
- // --- Ландшафт (Земля/Снег) ---
224
- const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 200, height: 200, subdivisions: 50 }, scene);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  const groundMaterial = new BABYLON.StandardMaterial("groundMat", scene);
226
- // Простая текстура снега из Playground для примера
227
- groundMaterial.diffuseTexture = new BABYLON.Texture("https://playground.babylonjs.com/textures/snow.jpg", scene);
228
- groundMaterial.diffuseTexture.uScale = 10;
229
- groundMaterial.diffuseTexture.vScale = 10;
230
- groundMaterial.specularColor = new BABYLON.Color3(0.1, 0.1, 0.1); // Небольшой блик
231
- groundMaterial.freeze(); // Оптимизация
 
 
 
 
 
232
  ground.material = groundMaterial;
233
- ground.receiveShadows = true;
234
-
235
- // --- Плейсхолдер бутылки ---
236
- // !!! ЗАМЕНИТЬ НА РЕАЛЬНУЮ МОДЕЛЬ !!!
237
- const bottlePlaceholder = BABYLON.MeshBuilder.CreateCylinder("bottle", { height: 4, diameterTop: 0.8, diameterBottom: 1, tessellation: 24 }, scene);
238
- bottlePlaceholder.position = new BABYLON.Vector3(0, 2, 0); // Позиция в центре
239
- const bottleMaterial = new BABYLON.StandardMaterial("bottleMat", scene);
240
- bottleMaterial.diffuseColor = new BABYLON.Color3(0.6, 0.8, 1.0); // Голубоватый цвет
241
- bottleMaterial.alpha = 0.7; // Полупрозрачность
242
- bottleMaterial.specularColor = new BABYLON.Color3(0.8, 0.9, 1);
243
- bottleMaterial.specularPower = 64;
244
- // Добавляем отражение окружения для эффекта стекла/льда
245
- bottleMaterial.reflectionTexture = skyboxMaterial.reflectionTexture;
246
- bottleMaterial.reflectionFresnelParameters = new BABYLON.FresnelParameters();
247
- bottleMaterial.reflectionFresnelParameters.bias = 0.1;
248
- bottleMaterial.reflectionFresnelParameters.power = 2;
249
- bottlePlaceholder.material = bottleMaterial;
250
- shadowGenerator.addShadowCaster(bottlePlaceholder); // Бутылка отбрасывает тень
251
-
252
- // --- Простые элементы окружения (Ледяные глыбы/Горы) ---
253
- const iceMaterial = new BABYLON.StandardMaterial("iceMat", scene);
254
- iceMaterial.diffuseColor = new BABYLON.Color3(0.8, 0.9, 1.0);
255
- iceMaterial.alpha = 0.85;
256
- iceMaterial.specularColor = new BABYLON.Color3(1, 1, 1);
257
- iceMaterial.reflectionTexture = skyboxMaterial.reflectionTexture; // Отражения
258
- iceMaterial.reflectionFresnelParameters = new BABYLON.FresnelParameters();
259
- iceMaterial.reflectionFresnelParameters.bias = 0.3;
260
- iceMaterial.reflectionFresnelParameters.power = 1.5;
261
-
262
- const mountainPositions = [
263
- new BABYLON.Vector3(-40, 0, 50),
264
- new BABYLON.Vector3(50, 0, 30),
265
- new BABYLON.Vector3(0, 0, 70),
266
- new BABYLON.Vector3(-60, 0, -20),
267
- new BABYLON.Vector3(30, 0, -50),
268
- ];
269
-
270
- mountainPositions.forEach((pos, i) => {
271
- const size = 20 + Math.random() * 30;
272
- const mountain = BABYLON.MeshBuilder.CreateSphere(`ice_${i}`, {diameter: size}, scene);
273
- mountain.position = pos;
274
- mountain.scaling.y = 0.8 + Math.random() * 1.2; // Приплюснуть или вытянуть
275
- mountain.scaling.x = 1 + Math.random() * 0.5;
276
- mountain.scaling.z = 1 + Math.random() * 0.5;
277
- mountain.material = iceMaterial;
278
- shadowGenerator.addShadowCaster(mountain);
279
- mountain.receiveShadows = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  });
281
 
282
- // --- Добавить эффект легкого тумана/дымки ---
283
- scene.fogMode = BABYLON.Scene.FOGMODE_EXP;
284
- scene.fogDensity = 0.006;
285
- scene.fogColor = new BABYLON.Color3(0.5, 0.7, 0.9);
286
 
287
  return scene;
288
  }
289
 
290
- // --- Логика навигации и анимации ---
291
-
292
- // Определяем точки (стадии) обзора
293
- const stages = [
294
- { // 0: Начальный вид издалека
295
- cameraPos: new BABYLON.Vector3(0, 8, -25),
296
- cameraTarget: new BABYLON.Vector3(0, 2, 0),
297
- infoId: 'welcome'
298
- },
299
- { // 1: Ближе к источнику (условному)
300
- cameraPos: new BABYLON.Vector3(-15, 6, 15),
301
- cameraTarget: new BABYLON.Vector3(-5, 1, 5), // Смотрим чуть в сторону
302
- infoId: 'source'
303
- },
304
- { // 2: Вид на "чистоту" ландшафта
305
- cameraPos: new BABYLON.Vector3(20, 10, 20),
306
- cameraTarget: new BABYLON.Vector3(0, 0, 0), // Смотрим на центр
307
- infoId: 'purity'
308
- },
309
- { // 3: Крупный план бутылки
310
- cameraPos: new BABYLON.Vector3(0, 3.5, -6),
311
- cameraTarget: new BABYLON.Vector3(0, 2, 0), // Смотрим на бутылку
312
- infoId: 'bottle'
313
- }
314
- ];
315
-
316
- function updateUI(stageIndex) {
317
- // Сначала скрыть все инфо-блоки
318
- Object.values(infoOverlays).forEach(el => el.classList.remove('visible'));
319
-
320
- // Показать нужный блок
321
- const currentInfoId = stages[stageIndex]?.infoId;
322
- if (currentInfoId && infoOverlays[currentInfoId]) {
323
- infoOverlays[currentInfoId].classList.add('visible');
324
- }
325
-
326
- // Обновить состояние кнопок
327
- prevBtn.disabled = stageIndex === 0;
328
- nextBtn.disabled = stageIndex === stages.length - 1;
329
- }
330
 
331
- function animateCameraToStage(stageIndex) {
332
- const targetStage = stages[stageIndex];
333
- if (!targetStage || !camera) return;
334
-
335
- const frameRate = 60;
336
- const totalFrames = animationDuration * frameRate;
337
-
338
- // Анимация позиции камеры
339
- const camPosAnimation = new BABYLON.Animation(
340
- "camPosAnim",
341
- "position",
342
- frameRate,
343
- BABYLON.Animation.ANIMATIONTYPE_VECTOR3,
344
- BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
345
- );
346
- const keysPos = [];
347
- keysPos.push({ frame: 0, value: camera.position });
348
- keysPos.push({ frame: totalFrames, value: targetStage.cameraPos });
349
- camPosAnimation.setKeys(keysPos);
350
-
351
- // Добавляем Easing Function для плавности
352
- const easingFunction = new BABYLON.QuadraticEase();
353
- easingFunction.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);
354
- camPosAnimation.setEasingFunction(easingFunction);
355
-
356
-
357
- // Анимация цели (точки, куда смотрит камера)
358
- const camTargetAnimation = new BABYLON.Animation(
359
- "camTargetAnim",
360
- "target", // Используем setTarget для UniversalCamera
361
- frameRate,
362
- BABYLON.Animation.ANIMATIONTYPE_VECTOR3,
363
- BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
364
- );
365
- const keysTarget = [];
366
- keysTarget.push({ frame: 0, value: camera.getTarget() }); // Текущая цель
367
- keysTarget.push({ frame: totalFrames, value: targetStage.cameraTarget });
368
- camTargetAnimation.setKeys(keysTarget);
369
- camTargetAnimation.setEasingFunction(easingFunction); // Та же плавность
370
-
371
- // Запускаем обе анимации
372
- scene.beginDirectAnimation(camera, [camPosAnimation, camTargetAnimation], 0, totalFrames, false, 1, () => {
373
- // Коллбэк после завершения анимации
374
- camera.setTarget(targetStage.cameraTarget); // Убедимся, что цель установлена точно
375
- updateUI(stageIndex); // Показать UI после завершения перехода
376
  });
377
- }
378
-
379
- // --- Инициализация и обработчики событий ---
380
- scene = createScene();
381
 
382
- // Начать рендеринг
383
- engine.runRenderLoop(function () {
384
- if (scene && scene.activeCamera) {
385
- scene.render();
386
- }
387
- });
388
 
389
- // Обработка изменения размера окна
390
- window.addEventListener('resize', function () {
391
  engine.resize();
392
  });
393
 
394
- // Обработчики кнопок
395
- nextBtn.addEventListener('click', () => {
396
- if (currentStage < stages.length - 1) {
397
- currentStage++;
398
- animateCameraToStage(currentStage);
399
- updateUI(currentStage); // Можно обновить кнопки сразу, не дожидаясь конца анимации
400
- }
401
- });
402
-
403
- prevBtn.addEventListener('click', () => {
404
- if (currentStage > 0) {
405
- currentStage--;
406
- animateCameraToStage(currentStage);
407
- updateUI(currentStage);
408
- }
409
- });
410
-
411
- // Показать начальный UI после небольшой задержки (когда сцена уже видна)
412
- scene.executeWhenReady(() => {
413
- engine.hideLoadingUI(); // Скрыть экран загрузки
414
- updateUI(currentStage); // Показать первый блок информации
415
- });
416
-
417
  </script>
418
 
419
  </body>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
  <title>Arctic Mineral Water - 3D Презентация</title>
7
+ <script src="https://cdn.babylonjs.com/babylon.js"></script>
8
+ <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
9
+ <script src="https://code.jquery.com/pep/0.4.3/pep.js"></script> <!-- Для поддержки pointer events -->
10
+
11
  <style>
12
  html, body {
13
  overflow: hidden;
 
15
  height: 100%;
16
  margin: 0;
17
  padding: 0;
18
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
19
+ background-color: #000; /* Фон на случай медленной загрузки */
20
+ color: #fff;
21
  }
22
 
23
  #renderCanvas {
24
  width: 100%;
25
  height: 100%;
26
+ touch-action: none; /* Важно для PEP */
27
  }
28
 
29
+ #loadingScreen {
 
30
  position: absolute;
31
+ top: 0;
32
+ left: 0;
33
+ width: 100%;
34
+ height: 100%;
35
+ background-color: #1a237e; /* Глубокий синий */
36
  color: white;
37
+ display: flex;
38
+ justify-content: center;
39
+ align-items: center;
40
+ font-size: 2em;
41
+ z-index: 100;
42
+ transition: opacity 1s ease-out;
43
+ }
44
+ #loadingScreen.hidden {
45
  opacity: 0;
46
+ pointer-events: none;
 
47
  }
48
 
49
+ #ui-overlay {
50
+ position: absolute;
51
+ bottom: 0;
52
+ left: 0;
53
+ width: 100%;
54
+ padding: 15px;
55
+ box-sizing: border-box;
56
+ display: flex;
57
+ justify-content: center; /* Центрируем кнопки навигации */
58
+ align-items: center;
59
+ z-index: 10;
60
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0) 100%);
61
+ flex-wrap: wrap; /* Перенос кнопок на мобильных */
62
+ gap: 10px; /* Расстояние между кнопками */
63
  }
64
 
65
+ .nav-button {
66
+ padding: 10px 15px;
67
+ background-color: rgba(25, 118, 210, 0.8); /* Синий с прозрачностью */
68
+ color: white;
69
+ border: 1px solid rgba(255, 255, 255, 0.5);
70
+ border-radius: 5px;
71
+ cursor: pointer;
72
+ font-size: 0.9em;
73
+ transition: background-color 0.3s ease, transform 0.1s ease;
74
+ text-align: center;
75
  }
76
 
77
+ .nav-button:hover, .nav-button:focus {
78
+ background-color: rgba(13, 71, 161, 0.9); /* Темнее при наведении */
79
+ outline: none;
80
  }
81
+ .nav-button:active {
82
+ transform: scale(0.95);
83
+ }
84
 
85
+ #content-display {
 
86
  position: absolute;
87
+ top: 20px;
88
  left: 50%;
89
  transform: translateX(-50%);
90
+ width: 80%;
91
+ max-width: 600px;
92
+ background-color: rgba(0, 20, 50, 0.85); /* Темно-синий фон */
93
+ padding: 20px;
94
+ border-radius: 8px;
95
+ border: 1px solid rgba(100, 181, 246, 0.7); /* Светло-голубая рамка */
96
+ z-index: 5;
97
+ opacity: 0;
98
+ transition: opacity 0.8s ease-in-out;
99
+ pointer-events: none; /* Не мешает взаимодействию с 3D если нужно */
100
+ text-align: center;
101
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
102
  }
103
 
104
+ #content-display.visible {
105
+ opacity: 1;
 
 
 
 
 
 
 
 
106
  }
107
 
108
+ #content-display h2 {
109
+ margin-top: 0;
110
+ color: #e3f2fd; /* Очень светлый голубой */
111
+ border-bottom: 1px solid rgba(100, 181, 246, 0.5);
112
+ padding-bottom: 10px;
113
+ margin-bottom: 15px;
114
+ }
115
+
116
+ #content-display p {
117
+ margin-bottom: 0;
118
+ color: #bbdefb; /* Светло-голубой */
119
+ line-height: 1.6;
120
+ }
121
 
122
+ /* Стилизация логотипа (опционально) */
123
+ #logo {
124
+ position: absolute;
125
+ top: 15px;
126
+ left: 15px;
127
+ font-size: 1.5em;
128
+ font-weight: bold;
129
+ color: #fff;
130
+ z-index: 11;
131
+ text-shadow: 1px 1px 3px rgba(0,0,0,0.7);
132
  }
133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  </style>
 
 
 
 
 
 
 
135
  </head>
136
  <body>
137
 
138
+ <div id="loadingScreen">
139
+ <div>Загрузка Arctic 3D...</div>
140
+ </div>
141
 
142
  <canvas id="renderCanvas"></canvas>
143
 
144
+ <div id="logo">Arctic</div>
145
+
146
+ <div id="ui-overlay">
147
+ <button class="nav-button" data-target="welcome">Главная</button>
148
+ <button class="nav-button" data-target="source">Источник</button>
149
+ <button class="nav-button" data-target="product">Наш Продукт</button>
150
+ <button class="nav-button" data-target="purity">Чистота</button>
 
 
 
 
 
 
 
 
 
151
  </div>
152
 
153
+ <div id="content-display">
154
+ <h2></h2>
155
+ <p></p>
 
156
  </div>
157
 
158
  <script>
159
+ const canvas = document.getElementById("renderCanvas");
 
160
  const loadingScreen = document.getElementById('loadingScreen');
161
+ const contentDisplay = document.getElementById('content-display');
162
+ const contentTitle = contentDisplay.querySelector('h2');
163
+ const contentText = contentDisplay.querySelector('p');
164
+
165
+ // --- Контент для секций ---
166
+ const sectionContent = {
167
+ welcome: {
168
+ title: "Добро пожаловать в мир Arctic",
169
+ text: "Откройте для себя чистоту и свежесть арктических ледников в каждой капле. Наша вода - это дар природы, бережно сохраненный для вас."
 
 
 
 
 
 
 
 
 
170
  },
171
+ source: {
172
+ title: "Наш Источник",
173
+ text: "Вода 'Arctic' добывается из реликтового подледного озера в экологически чистом регионе Арктики. Уникальный минеральный состав и природная фильтрация."
 
174
  },
175
+ product: {
176
+ title: "Вода Arctic",
177
+ text: "Идеально сбалансированная минеральная вода. Представлена в уникальной бутылке, напоминающей кристалл льда. Освежает и наполняет энергией."
178
+ },
179
+ purity: {
180
+ title: "Гарантия Чистоты",
181
+ text: "Современные технологии и строгий контроль качества на каждом этапе производства гарантируют первозданную чистоту и безопасность нашей воды."
182
+ }
183
  };
184
 
185
+ // --- Инициализация Babylon.js ---
186
+ const engine = new BABYLON.Engine(canvas, true, { stencil: true, preserveDrawingBuffer: true }, true);
187
+ engine.displayLoadingUI = function() { /* Отключаем стандартный загрузчик */ };
188
+
189
+ let scene;
190
+ let camera;
191
 
192
+ // --- Создание сцены ---
193
  function createScene() {
194
  scene = new BABYLON.Scene(engine);
195
+ scene.clearColor = new BABYLON.Color3.FromHexString("#051937"); // Темно-синий фон
196
 
197
  // --- Камера ---
198
+ // Используем UniversalCamera для возможности управления (хотя здесь оно отключено)
199
+ // и для простоты анимации position/target
200
  camera = new BABYLON.UniversalCamera("camera", new BABYLON.Vector3(0, 5, -20), scene);
201
+ camera.setTarget(BABYLON.Vector3.Zero()); // Смотрим в центр вначале
202
+ camera.attachControl(canvas, false); // Отключаем стандартное управление камерой
203
+ camera.speed = 0.5;
204
+ camera.minZ = 0.5; // Важно для близких объектов и скайбокса
205
 
206
+ // --- Свет ---
207
+ const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
208
  light.intensity = 0.8;
209
  light.groundColor = new BABYLON.Color3(0.5, 0.7, 1); // Холодный цвет снизу
 
 
 
 
 
 
 
210
 
211
+ const pointLight = new BABYLON.PointLight("pointLight", new BABYLON.Vector3(0, 10, -10), scene);
212
+ pointLight.intensity = 0.5;
 
 
 
213
 
214
+ // --- Скайбокс (Арктическая тема) ---
215
  const skybox = BABYLON.MeshBuilder.CreateBox("skyBox", { size: 1000.0 }, scene);
216
+ const skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
217
+ // Используем текстуру с CDN (замените на свою, если нужно)
218
+ // Пример текстуры северного сияния:
219
+ skyboxMaterial.backFaceCulling = false;
220
+ // Можно использовать CubeTexture с 6 изображениями для лучшего результата
221
+ // Пример: https://www.babylonjs-playground.com/textures/skybox_nx.jpg и т.д.
222
+ // Для простоты используем одну текстуру на все стороны
223
+ skyboxMaterial.reflectionTexture = new BABYLON.Texture("https://playground.babylonjs.com/textures/environment.env", scene); // Пример .env текстуры
224
  skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
225
+ skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0); // Не нужен цвет, если есть отражение/эмиссия
226
  skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
227
+ skyboxMaterial.disableLighting = true; // Не зависит от освещения сцены
228
  skybox.material = skyboxMaterial;
229
+ skybox.infiniteDistance = true; // Фикс для рендеринга
230
+
231
+
232
+ // --- Объекты сцены (представляют секции) ---
233
+
234
+ // 1. Приветствие (Центральная точка)
235
+ const welcomeAnchor = new BABYLON.TransformNode("welcomeAnchor", scene);
236
+ welcomeAnchor.position = new BABYLON.Vector3(0, 2, 0);
237
+ // Можно добавить сюда логотип Arctic или абстрактную фигуру
238
+ const welcomeSphere = BABYLON.MeshBuilder.CreateSphere("welcomeSphere", {diameter: 3}, scene);
239
+ welcomeSphere.material = new BABYLON.StandardMaterial("welcomeMat", scene);
240
+ welcomeSphere.material.diffuseColor = new BABYLON.Color3(0.8, 0.9, 1);
241
+ welcomeSphere.material.alpha = 0.7;
242
+ welcomeSphere.parent = welcomeAnchor;
243
+
244
+
245
+ // 2. Источник (Ледяная пещера / скала - символически)
246
+ const sourceAnchor = new BABYLON.TransformNode("sourceAnchor", scene);
247
+ sourceAnchor.position = new BABYLON.Vector3(-15, 3, 5);
248
+ const sourceShape = BABYLON.MeshBuilder.CreateIcoSphere("sourceShape", {radius: 2.5, subdivisions: 2}, scene);
249
+ sourceShape.material = new BABYLON.StandardMaterial("sourceMat", scene);
250
+ sourceShape.material.diffuseColor = new BABYLON.Color3(0.5, 0.7, 0.9);
251
+ sourceShape.material.wireframe = true; // Эффект кристаллической решетки
252
+ sourceShape.parent = sourceAnchor;
253
+
254
+
255
+ // 3. Продукт (Бутылка - здесь примитив, нужна модель)
256
+ const productAnchor = new BABYLON.TransformNode("productAnchor", scene);
257
+ productAnchor.position = new BABYLON.Vector3(15, 2.5, 8);
258
+ // Имитация бутылки цилиндром
259
+ const bottle = BABYLON.MeshBuilder.CreateCylinder("bottle", {height: 4, diameterTop: 0.8, diameterBottom: 1.2}, scene);
260
+ bottle.material = new BABYLON.StandardMaterial("bottleMat", scene);
261
+ bottle.material.diffuseColor = new BABYLON.Color3(0.7, 0.85, 1.0);
262
+ bottle.material.alpha = 0.8; // Полупрозрачность
263
+ bottle.parent = productAnchor;
264
+
265
+ // Добавим эффект "воды" или "чистоты" - частицы
266
+ const particleSystem = new BABYLON.ParticleSystem("particles", 2000, scene);
267
+ particleSystem.particleTexture = new BABYLON.Texture("https://playground.babylonjs.com/textures/flare.png", scene); // Текстура частицы
268
+ particleSystem.emitter = productAnchor; // Эмиттер - точка у "бутылки"
269
+ particleSystem.minEmitBox = new BABYLON.Vector3(-0.5, 2, -0.5); // Область старта частиц
270
+ particleSystem.maxEmitBox = new BABYLON.Vector3(0.5, 2.5, 0.5);
271
+ particleSystem.color1 = new BABYLON.Color4(0.7, 0.8, 1.0, 1.0);
272
+ particleSystem.color2 = new BABYLON.Color4(0.2, 0.5, 1.0, 1.0);
273
+ particleSystem.colorDead = new BABYLON.Color4(0, 0, 0.2, 0.0);
274
+ particleSystem.minSize = 0.1;
275
+ particleSystem.maxSize = 0.3;
276
+ particleSystem.minLifeTime = 0.5;
277
+ particleSystem.maxLifeTime = 2.0;
278
+ particleSystem.emitRate = 300;
279
+ particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;
280
+ particleSystem.gravity = new BABYLON.Vector3(0, -1.0, 0); // Легкое падение
281
+ particleSystem.direction1 = new BABYLON.Vector3(-1, 2, -1);
282
+ particleSystem.direction2 = new BABYLON.Vector3(1, 2, 1);
283
+ particleSystem.minAngularSpeed = 0;
284
+ particleSystem.maxAngularSpeed = Math.PI;
285
+ particleSystem.targetStopDuration = 0;
286
+ particleSystem.minEmitPower = 1;
287
+ particleSystem.maxEmitPower = 3;
288
+ particleSystem.updateSpeed = 0.01;
289
+ particleSystem.start();
290
+
291
+
292
+ // 4. Чистота (Символическая структура)
293
+ const purityAnchor = new BABYLON.TransformNode("purityAnchor", scene);
294
+ purityAnchor.position = new BABYLON.Vector3(0, 4, 20); // Дальше
295
+ const purityShape = BABYLON.MeshBuilder.CreateTorus("purityShape", {diameter: 4, thickness: 0.3, tessellation: 64}, scene);
296
+ purityShape.material = new BABYLON.StandardMaterial("purityMat", scene);
297
+ purityShape.material.diffuseColor = new BABYLON.Color3(1, 1, 1);
298
+ purityShape.material.emissiveColor = new BABYLON.Color3(0.5, 0.8, 1); // Свечение
299
+ purityShape.parent = purityAnchor;
300
+ purityShape.rotation.x = Math.PI / 2; // Повернуть для вида
301
+
302
+ // --- "Земля" или ледяная поверхность ---
303
+ const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 50, height: 50}, scene);
304
  const groundMaterial = new BABYLON.StandardMaterial("groundMat", scene);
305
+ // Текстура льда/снега (пример)
306
+ const groundTexture = new BABYLON.Texture("https://playground.babylonjs.com/textures/ground.jpg", scene); // Замените на текстуру льда/снега
307
+ groundTexture.uScale = 5;
308
+ groundTexture.vScale = 5;
309
+ groundMaterial.diffuseTexture = groundTexture;
310
+ groundMaterial.specularColor = new BABYLON.Color3(0.2, 0.2, 0.2); // Блики льда
311
+ groundMaterial.reflectionTexture = new BABYLON.MirrorTexture("mirror", 512, scene, true);
312
+ groundMaterial.reflectionTexture.mirrorPlane = new BABYLON.Plane(0, -1.0, 0, 0); // Отражение от Y=0
313
+ groundMaterial.reflectionTexture.renderList = scene.meshes.filter(m => m !== ground && m !== skybox); // Отражать все, кроме земли и скайбокса
314
+ groundMaterial.reflectionFresnelParameters = new BABYLON.FresnelParameters();
315
+ groundMaterial.reflectionFresnelParameters.bias = 0.1; // Небольшое отражение
316
  ground.material = groundMaterial;
317
+ ground.position.y = -0.1; // Чуть ниже нуля
318
+
319
+
320
+ // --- Анимация и навигация ---
321
+ const targets = {
322
+ welcome: { pos: new BABYLON.Vector3(0, 4, -15), target: welcomeAnchor.position },
323
+ source: { pos: new BABYLON.Vector3(-12, 5, -2), target: sourceAnchor.position },
324
+ product: { pos: new BABYLON.Vector3(12, 4, 0), target: productAnchor.position },
325
+ purity: { pos: new BABYLON.Vector3(0, 6, 12), target: purityAnchor.position }
326
+ };
327
+
328
+ function navigateTo(targetName) {
329
+ const targetInfo = targets[targetName];
330
+ if (!targetInfo) return;
331
+
332
+ // Скрываем старый контент
333
+ contentDisplay.classList.remove('visible');
334
+
335
+ // Плавная анимация камеры
336
+ const frameRate = 60;
337
+ const duration = 1.5; // Секунды
338
+
339
+ const camPosAnim = new BABYLON.Animation("camPosAnim", "position", frameRate, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
340
+ const camTargetAnim = new BABYLON.Animation("camTargetAnim", "target", frameRate, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
341
+
342
+ const keysPos = [];
343
+ keysPos.push({ frame: 0, value: camera.position });
344
+ keysPos.push({ frame: frameRate * duration, value: targetInfo.pos });
345
+ camPosAnim.setKeys(keysPos);
346
+
347
+ // Для UniversalCamera анимируем не target, а rotationQuaternion или rotation
348
+ // Проще анимировать сам target Vector3, к которому камера привязана
349
+ // Создадим временный анимируемый таргет
350
+ const currentTarget = camera.getTarget();
351
+ const targetAnimNode = new BABYLON.TransformNode("targetAnimNode", scene);
352
+ targetAnimNode.position = currentTarget.clone();
353
+ camera.setTarget(targetAnimNode.position); // Привязываем камеру к ноде
354
+
355
+ const targetPosAnim = new BABYLON.Animation("targetPosAnim", "position", frameRate, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
356
+ const keysTargetPos = [];
357
+ keysTargetPos.push({ frame: 0, value: targetAnimNode.position });
358
+ keysTargetPos.push({ frame: frameRate * duration, value: targetInfo.target });
359
+ targetPosAnim.setKeys(keysTargetPos);
360
+
361
+
362
+ // Добавляем Easing Function для плавности
363
+ const easingFunction = new BABYLON.CubicEase();
364
+ easingFunction.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);
365
+ camPosAnim.setEasingFunction(easingFunction);
366
+ targetPosAnim.setEasingFunction(easingFunction);
367
+
368
+ // Запускаем анимации
369
+ scene.beginDirectAnimation(camera, [camPosAnim], 0, frameRate * duration, false);
370
+ scene.beginDirectAnimation(targetAnimNode, [targetPosAnim], 0, frameRate * duration, false, () => {
371
+ // По завершению анимации показываем новый контент
372
+ const content = sectionContent[targetName];
373
+ contentTitle.textContent = content.title;
374
+ contentText.textContent = content.text;
375
+ contentDisplay.classList.add('visible');
376
+ // Отвязываем камеру от временной ноды и устанавливаем финальный таргет
377
+ // Хотя можно и оставить привязанной, если не планируется другого управления
378
+ camera.setTarget(targetInfo.target);
379
+ targetAnimNode.dispose(); // Удаляем временную ноду
380
+ });
381
+ }
382
+
383
+ // --- Привязка кнопок ---
384
+ const buttons = document.querySelectorAll('.nav-button');
385
+ buttons.forEach(button => {
386
+ button.addEventListener('click', () => {
387
+ const target = button.getAttribute('data-target');
388
+ navigateTo(target);
389
+ });
390
+ button.addEventListener('touchstart', (e) => { // Для лучшего отклика на мобильных
391
+ e.preventDefault(); // Предотвратить эмуляцию мыши
392
+ button.click();
393
+ }, { passive: false });
394
+ });
395
+
396
+ // --- Инициализация первого вида ---
397
+ setTimeout(() => { // Небольшая задержка для показа сцены перед анимацией
398
+ navigateTo('welcome');
399
+ }, 500);
400
+
401
+
402
+ // --- Дополнительные эффекты (опционально) ---
403
+ // Легкое вращение объектов для динамики
404
+ scene.registerBeforeRender(() => {
405
+ if(welcomeSphere) welcomeSphere.rotation.y += 0.002;
406
+ if(sourceShape) sourceShape.rotation.x += 0.003;
407
+ if(bottle) bottle.rotation.y -= 0.005;
408
+ if(purityShape) purityShape.rotation.z += 0.004;
409
  });
410
 
 
 
 
 
411
 
412
  return scene;
413
  }
414
 
415
+ // --- Запуск ---
416
+ scene = createScene();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
 
418
+ // --- Обработка готовности сцены и скрытие загрузчика ---
419
+ scene.executeWhenReady(() => {
420
+ engine.runRenderLoop(function () {
421
+ if (scene && scene.activeCamera) {
422
+ scene.render();
423
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
  });
425
+ // Плавно скрываем загрузочный экран
426
+ loadingScreen.classList.add('hidden');
427
+ });
 
428
 
 
 
 
 
 
 
429
 
430
+ // --- Адаптация под размер окна ---
431
+ window.addEventListener("resize", function () {
432
  engine.resize();
433
  });
434
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
  </script>
436
 
437
  </body>