Aleksmorshen commited on
Commit
626501e
·
verified ·
1 Parent(s): f6f9e61

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +321 -356
index.html CHANGED
@@ -3,11 +3,7 @@
3
  <head>
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;
@@ -16,422 +12,391 @@
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>
 
3
  <head>
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 - Чистейшая Минеральная Вода</title>
 
 
 
 
7
  <style>
8
  html, body {
9
  overflow: hidden;
 
12
  margin: 0;
13
  padding: 0;
14
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
15
+ color: #fff; /* Белый текст по умолчанию */
16
+ background-color: #001f3f; /* Темно-синий фон на случай долгой загрузки */
17
  }
18
 
19
  #renderCanvas {
20
  width: 100%;
21
  height: 100%;
22
+ touch-action: none; /* Отключает стандартные действия браузера при касании (скролл, зум) */
23
+ display: block; /* Убирает лишний отступ снизу */
 
 
24
  position: absolute;
25
  top: 0;
26
  left: 0;
27
+ z-index: 1;
 
 
 
 
 
 
 
 
 
28
  }
29
+
30
+ /* Стили для контентных блоков */
31
+ .content-section {
32
+ position: absolute;
33
+ top: 50%;
34
+ left: 50%;
35
+ transform: translate(-50%, -50%);
36
+ z-index: 2; /* Поверх канваса */
37
+ background-color: rgba(0, 31, 63, 0.7); /* Полупрозрачный темно-синий фон */
38
+ padding: 30px;
39
+ border-radius: 15px;
40
+ max-width: 80%;
41
+ width: 500px; /* Или другая ширина */
42
+ text-align: center;
43
  opacity: 0;
44
+ visibility: hidden;
45
+ transition: opacity 0.8s ease-in-out, visibility 0.8s ease-in-out;
46
+ box-shadow: 0 5px 25px rgba(0, 0, 0, 0.4);
47
  }
48
 
49
+ .content-section.active {
50
+ opacity: 1;
51
+ visibility: visible;
 
 
 
 
 
 
 
 
 
 
 
52
  }
53
 
54
+ .content-section h2 {
55
+ margin-top: 0;
56
+ font-size: 2.5em;
57
+ color: #7FDBFF; /* Светло-голубой акцент */
58
+ text-shadow: 0 0 10px rgba(127, 219, 255, 0.5);
 
 
 
 
 
59
  }
60
 
61
+ .content-section p {
62
+ font-size: 1.1em;
63
+ line-height: 1.6;
64
  }
 
 
 
65
 
66
+ /* Стили для мобильных контролов */
67
+ #mobile-controls {
68
  position: absolute;
69
+ bottom: 20px;
70
  left: 50%;
71
  transform: translateX(-50%);
72
+ z-index: 3; /* Поверх всего */
73
+ display: none; /* По умолчанию скрыты, показываем через JS если тачскрин */
74
+ background-color: rgba(0, 31, 63, 0.8);
75
+ padding: 10px 20px;
76
+ border-radius: 25px;
 
 
 
 
 
 
 
 
 
 
 
77
  }
78
 
79
+ #mobile-controls button {
80
+ background-color: #7FDBFF;
81
+ color: #001f3f;
82
+ border: none;
83
+ padding: 12px 25px;
84
+ margin: 0 10px;
85
+ border-radius: 20px;
86
+ font-size: 1.2em;
87
+ font-weight: bold;
88
+ cursor: pointer;
89
+ transition: background-color 0.3s ease, transform 0.1s ease;
90
+ }
91
 
92
+ #mobile-controls button:active {
93
+ background-color: #ffffff;
94
+ transform: scale(0.95);
95
+ }
 
96
 
97
+ /* Стиль для индикатора загрузки Babylon.js */
98
+ .babylonLoadingScreenContainer {
99
+ background-color: #001f3f !important;
100
+ color: #7FDBFF !important;
101
+ }
102
+ .babylonLoadingScreenContainer .babylonLoadingScreenText {
103
+ font-size: 1.5em !important;
 
 
 
104
  }
105
 
106
  </style>
107
+ <!-- Подключение Babylon.js -->
108
+ <script src="https://cdn.babylonjs.com/babylon.js"></script>
109
+ <!-- Подключение материалов (для WaterMaterial) -->
110
+ <script src="https://cdn.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
111
+ <!-- Подключение загрузчиков (если будете использовать 3D модели) -->
112
+ <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
113
  </head>
114
  <body>
115
 
116
+ <canvas id="renderCanvas"></canvas>
117
+
118
+ <!-- Контентные блоки (скрыты по умолчанию) -->
119
+ <div id="section-home" class="content-section">
120
+ <h2>Arctic</h2>
121
+ <p>Чистота северных ледников в каждой капле. Откройте для себя истинный вкус природы.</p>
122
  </div>
123
 
124
+ <div id="section-source" class="content-section">
125
+ <h2>Наш Источник</h2>
126
+ <p>Вода "Arctic" добывается из реликтового подземного озера, защищенного вечной мерзлотой. Уникальная природная фильтрация дарит ей кристальную чистоту и сбалансированный минеральный состав.</p>
127
+ </div>
128
 
129
+ <div id="section-purity" class="content-section">
130
+ <h2>Идеальная Чистота</h2>
131
+ <p>Современные технологии розлива в сочетании с природной чистотой источника гарантируют сохранение всех полезных свойств воды "Arctic" без добавления консервантов.</p>
132
+ </div>
133
 
134
+ <div id="section-product" class="content-section">
135
+ <h2>Наш Продукт</h2>
136
+ <p>Попробуйте "Arctic" в удобной упаковке: классическая ПЭТ ��утылка 0.5л и 1.5л, а также премиальная стеклянная бутылка 0.75л для особых случаев.</p>
137
+ <!-- Сюда можно добавить изображения или 3D-превью бутылок -->
 
138
  </div>
139
 
140
+ <div id="section-contact" class="content-section">
141
+ <h2>Связаться с нами</h2>
142
+ <p>Узнайте больше о воде "Arctic" и условиях сотрудничества. <br> Телефон: +7 (XXX) XXX-XX-XX <br> Email: [email protected]</p>
143
  </div>
144
 
145
+ <!-- Мобильные кнопки навигации -->
146
+ <div id="mobile-controls">
147
+ <button id="prevBtn">< Назад</button>
148
+ <button id="nextBtn">Далее ></button>
149
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
+ <script>
152
+ const canvas = document.getElementById('renderCanvas');
153
  const engine = new BABYLON.Engine(canvas, true, { stencil: true, preserveDrawingBuffer: true }, true);
 
154
 
155
+ // --- Настройки сцены ---
156
  let scene;
157
  let camera;
158
+ let waterMaterial;
159
+ const sectionElements = [
160
+ document.getElementById('section-home'),
161
+ document.getElementById('section-source'),
162
+ document.getElementById('section-purity'),
163
+ document.getElementById('section-product'),
164
+ document.getElementById('section-contact')
165
+ ];
166
+ const sectionPositions = [
167
+ new BABYLON.Vector3(0, 5, -10), // Home (Начальная позиция)
168
+ new BABYLON.Vector3(0, 5, 50), // Source
169
+ new BABYLON.Vector3(50, 5, 100), // Purity
170
+ new BABYLON.Vector3(0, 5, 150), // Product
171
+ new BABYLON.Vector3(-50, 5, 200) // Contact
172
+ ];
173
+ const cameraLookAtOffsets = [ // Куда смотрит камера для каждой секции (относительно позиции камеры)
174
+ new BABYLON.Vector3(0, 0, 50), // Home смотрит вперед
175
+ new BABYLON.Vector3(0, 0, 50), // Source смотрит вперед
176
+ new BABYLON.Vector3(0, -2, 50),// Purity смотрит чуть вперед и вниз
177
+ new BABYLON.Vector3(0, 0, 50), // Product смотрит вперед
178
+ new BABYLON.Vector3(0, 0, 50) // Contact смотрит вперед
179
+ ];
180
+
181
+ let currentSectionIndex = 0;
182
+ let isAnimating = false; // Флаг для предотвращения спама навигации
183
+
184
+ // --- Функция создания сцены ---
185
+ const createScene = () => {
186
  scene = new BABYLON.Scene(engine);
187
+ scene.clearColor = new BABYLON.Color4(0.1, 0.2, 0.4, 1.0); // Глубокий синий цвет фона
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
+ // Камера
190
+ // Используем UniversalCamera для более гибкого управления взглядом
191
+ camera = new BABYLON.UniversalCamera("camera1", sectionPositions[0], scene);
192
+ camera.setTarget(sectionPositions[0].add(cameraLookAtOffsets[0])); // Устанавливаем начальный взгляд
193
+ // camera.attachControl(canvas, true); // Отключаем стандартное управление камерой
194
 
195
+ // Свет
196
+ const light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
197
+ light.intensity = 0.8;
198
+ const dirLight = new BABYLON.DirectionalLight("dirLight", new BABYLON.Vector3(-0.5, -1, -0.5), scene);
199
+ dirLight.position = new BABYLON.Vector3(20, 40, 20);
200
+ dirLight.intensity = 0.7;
201
+
202
+ // Вода (Используем WaterMaterial)
203
+ // Замените 'assets/waterbump.png' на ваш путь к текстуре нормалей воды
204
+ waterMaterial = new BABYLON.WaterMaterial("waterMaterial", scene, new BABYLON.Vector2(1024, 1024));
205
+ waterMaterial.bumpTexture = new BABYLON.Texture("assets/waterbump.png", scene); // Текстура для волн
206
+ waterMaterial.windForce = -5; // Сила ветра (влияет на скорость волн)
207
+ waterMaterial.waveHeight = 0.15; // Высота волн
208
+ waterMaterial.bumpHeight = 0.2; // Интенсивность рельефа
209
+ waterMaterial.waveLength = 0.1; // Длина волны
210
+ waterMaterial.waterColor = new BABYLON.Color3(0.1, 0.3, 0.6); // Цвет воды
211
+ waterMaterial.colorBlendFactor = 0.5; // Смешивание цвета воды и отражений/преломлений
212
+ waterMaterial.disableClipPlane = true; // Важно для UniversalCamera над водой
213
+
214
+ // Создаем водную поверхность
215
+ const waterMesh = BABYLON.MeshBuilder.CreateGround("waterMesh", {width: 1000, height: 1000, subdivisions: 32}, scene);
216
+ waterMesh.material = waterMaterial;
217
+ waterMesh.position.y = -0.5; // Немного ниже уровня камеры
218
+
219
+ // Skybox (Небо/Окружение)
220
+ // Замените 'assets/' на ваш путь и имена файлов skybox
221
+ const skybox = BABYLON.MeshBuilder.CreateBox("skyBox", {size: 1000.0}, scene);
222
  const skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
223
+ skyboxMaterial.backFaceCulling = false;
224
+ skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("assets/skybox", scene, ["_px.jpg", "_py.jpg", "_pz.jpg", "_nx.jpg", "_ny.jpg", "_nz.jpg"]);
 
 
 
 
 
225
  skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
226
+ skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
227
  skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
 
228
  skybox.material = skyboxMaterial;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
+ // Добавляем объекты в список рендеринга для воды (отражения/преломления)
231
+ waterMaterial.addToRenderList(skybox);
232
+ // Если добавите другие объекты (айсберги, бутылки), их тоже нужно добавить:
233
+ // waterMaterial.addToRenderList(icebergMesh);
234
+ // waterMaterial.addToRenderList(bottleModel);
235
+
236
+
237
+ // --- Плейсхолдер для 3D модели бутылки (замените на загрузку вашей модели) ---
238
+ const bottlePlaceholder = BABYLON.MeshBuilder.CreateCylinder("bottle", {height: 4, diameterTop: 0.8, diameterBottom: 1.2}, scene);
239
+ bottlePlaceholder.position = new BABYLON.Vector3(-5, 2, 155); // Позиция рядом с секцией "Продукт"
240
+ const bottleMat = new BABYLON.StandardMaterial("bottleMat", scene);
241
+ bottleMat.diffuseColor = new BABYLON.Color3(0.7, 0.85, 1); // Светло-голубой
242
+ bottleMat.alpha = 0.8; // Полупрозрачность
243
+ bottlePlaceholder.material = bottleMat;
244
+ waterMaterial.addToRenderList(bottlePlaceholder); // Добавить к отражениям воды
245
+
246
+ // Анимация вращения бутылки (опционально)
247
+ scene.registerBeforeRender(() => {
248
+ bottlePlaceholder.rotation.y += 0.01;
249
  });
250
+ // --- Конец плейсхолдера ---
251
 
252
+ // Показать первую секцию сразу
253
+ showSection(currentSectionIndex);
 
 
254
 
255
+ return scene;
256
+ };
257
 
258
+ // --- Функция отображения нужной секции ---
259
+ const showSection = (index) => {
260
+ sectionElements.forEach((el, i) => {
261
+ if (i === index) {
262
+ el.classList.add('active');
263
+ } else {
264
+ el.classList.remove('active');
265
+ }
266
+ });
267
+ };
268
 
269
+ // --- Функция навигации к секции ---
270
+ const navigateToSection = (index) => {
271
+ if (isAnimating || index < 0 || index >= sectionPositions.length) {
272
+ return; // Не анимировать, если уже анимируется или индекс вне диапазона
273
+ }
274
 
275
+ isAnimating = true;
276
+ const targetPosition = sectionPositions[index];
277
+ const targetLookAt = targetPosition.add(cameraLookAtOffsets[index]); // Целевая точка взгляда
278
+
279
+ // Плавный переход текста (начинаем скрывать текущий)
280
+ sectionElements[currentSectionIndex].classList.remove('active');
281
+
282
+ // Анимация позиции камеры
283
+ const animCamPos = new BABYLON.Animation(
284
+ "camPosAnim", "position", 60,
285
+ BABYLON.Animation.ANIMATIONTYPE_VECTOR3,
286
+ BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
287
+ );
288
+ const keysPos = [];
289
+ keysPos.push({ frame: 0, value: camera.position });
290
+ keysPos.push({ frame: 100, value: targetPosition }); // 100 кадров анимации
291
+ animCamPos.setKeys(keysPos);
292
+
293
+ // Анимация цели (взгляда) камеры
294
+ const animCamTarget = new BABYLON.Animation(
295
+ "camTargetAnim", "target", 60, // Используем 'target' для UniversalCamera
296
+ BABYLON.Animation.ANIMATIONTYPE_VECTOR3,
297
+ BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
298
+ );
299
+ const keysTarget = [];
300
+ keysTarget.push({ frame: 0, value: camera.getTarget() });
301
+ keysTarget.push({ frame: 100, value: targetLookAt });
302
+ animCamTarget.setKeys(keysTarget);
303
+
304
+
305
+ // Добавляем Easing Function для плавности
306
+ const easingFunction = new BABYLON.QuadraticEase();
307
+ easingFunction.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);
308
+ animCamPos.setEasingFunction(easingFunction);
309
+ animCamTarget.setEasingFunction(easingFunction);
310
+
311
+ // Запускаем анимации
312
+ scene.beginDirectAnimation(camera, [animCamPos, animCamTarget], 0, 100, false, 1, () => {
313
+ // По завершении анимации
314
+ currentSectionIndex = index;
315
+ showSection(currentSectionIndex); // Показываем новый текст
316
+ isAnimating = false;
317
+ // Обновляем кнопки на мобильных
318
+ updateMobileButtons();
319
+ });
320
+ };
321
 
322
+ // --- Обновление состояния кнопок на мобильных ---
323
+ const updateMobileButtons = () => {
324
+ const prevBtn = document.getElementById('prevBtn');
325
+ const nextBtn = document.getElementById('nextBtn');
326
+ if (!prevBtn || !nextBtn) return;
327
 
328
+ prevBtn.disabled = currentSectionIndex === 0 || isAnimating;
329
+ nextBtn.disabled = currentSectionIndex === sectionPositions.length - 1 || isAnimating;
330
+ };
331
+
332
+
333
+ // --- Инициализация ---
334
+ engine.displayLoadingUI(); // Показать экран загрузки
335
+ scene = createScene();
 
 
336
 
337
+ engine.runRenderLoop(() => {
338
+ if (scene) {
339
+ scene.render();
340
+ }
341
+ });
342
 
343
+ window.addEventListener('resize', () => {
 
344
  engine.resize();
345
  });
346
 
347
+ // --- Обработка ввода ---
348
+
349
+ // 1. Скролл мышью для десктопа
350
+ let scrollTimeout;
351
+ const handleScroll = (event) => {
352
+ if (isAnimating) return; // Игнорировать скролл во время анимации
353
+
354
+ // Предотвращаем слишком частое срабатывание
355
+ clearTimeout(scrollTimeout);
356
+ scrollTimeout = setTimeout(() => {
357
+ const delta = Math.sign(event.deltaY); // Определяем направление скролла
358
+ if (delta > 0) { // Скролл вниз
359
+ navigateToSection(currentSectionIndex + 1);
360
+ } else if (delta < 0) { // Скролл вверх
361
+ navigateToSection(currentSectionIndex - 1);
362
+ }
363
+ }, 100); // Небольшая задержка
364
+ };
365
+
366
+ // 2. Кнопки для мобильных
367
+ const setupMobileControls = () => {
368
+ const mobileControlsDiv = document.getElementById('mobile-controls');
369
+ const prevBtn = document.getElementById('prevBtn');
370
+ const nextBtn = document.getElementById('nextBtn');
371
+
372
+ // Проверяем, есть ли тач события (простой способ определить мобильное устройство)
373
+ const isMobile = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0);
374
+
375
+ if (isMobile && mobileControlsDiv && prevBtn && nextBtn) {
376
+ mobileControlsDiv.style.display = 'block';
377
+
378
+ prevBtn.addEventListener('click', () => navigateToSection(currentSectionIndex - 1));
379
+ nextBtn.addEventListener('click', () => navigateToSection(currentSectionIndex + 1));
380
+
381
+ // Обновляем состояние кнопок при загрузке
382
+ updateMobileButtons();
383
+
384
+ // Отключаем скролл мышью на мобильных, чтобы не конфликтовать
385
+ canvas.removeEventListener('wheel', handleScroll);
386
+
387
+ } else {
388
+ // Если не мобильное, используем скролл
389
+ canvas.addEventListener('wheel', handleScroll, { passive: false }); // passive: false чтобы можно было предотвратить стандартный скролл страницы, если понадобится
390
+ }
391
+ };
392
+
393
+
394
+ // Запускаем настройку контролов после того, как сцена готова
395
+ scene.executeWhenReady(() => {
396
+ engine.hideLoadingUI(); // Скрыть экран загрузки
397
+ setupMobileControls();
398
+ });
399
+
400
  </script>
401
 
402
  </body>