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

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +379 -325
index.html CHANGED
@@ -1,402 +1,456 @@
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, maximum-scale=1.0, user-scalable=no">
6
- <title>Arctic - Чистейшая Минеральная Вода</title>
7
  <style>
8
- html, body {
9
- overflow: hidden;
10
- width: 100%;
11
- height: 100%;
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>
 
1
  <!DOCTYPE html>
2
+ <html lang="en">
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 - Pure Refreshment</title>
7
  <style>
8
+ /* Basic Reset */
9
+ * {
 
 
10
  margin: 0;
11
  padding: 0;
12
+ box-sizing: border-box;
 
 
13
  }
14
 
15
+ html, body {
16
+ overflow: hidden; /* Prevent default scrollbars */
17
  width: 100%;
18
  height: 100%;
19
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
20
+ background-color: #051d30; /* Dark blue fallback */
21
+ color: #fff;
22
+ }
23
+
24
+ /* Canvas Styling */
25
+ #bg {
26
+ position: fixed;
27
  top: 0;
28
  left: 0;
29
+ width: 100%;
30
+ height: 100%;
31
+ z-index: -1; /* Behind other content */
32
+ display: block; /* Prevents potential inline spacing issues */
33
  }
34
 
35
+ /* Content Overlay Styling */
36
  .content-section {
37
+ position: fixed;
38
  top: 50%;
39
  left: 50%;
40
  transform: translate(-50%, -50%);
41
+ width: 80%;
42
+ max-width: 600px;
43
  padding: 30px;
44
+ background-color: rgba(15, 45, 70, 0.85); /* Semi-transparent dark blue */
45
  border-radius: 15px;
46
+ border: 1px solid rgba(173, 216, 230, 0.5); /* Light blue border */
47
+ box-shadow: 0 0 25px rgba(173, 216, 230, 0.3);
 
48
  opacity: 0;
49
  visibility: hidden;
50
+ transition: opacity 0.8s ease-out, visibility 0.8s;
51
+ text-align: center;
52
+ z-index: 10;
53
+ pointer-events: none; /* Allow clicking through initially */
54
  }
55
 
56
+ .content-section.visible {
57
  opacity: 1;
58
  visibility: visible;
59
+ pointer-events: auto; /* Allow interaction when visible */
60
  }
61
 
62
+ .content-section h1 {
 
63
  font-size: 2.5em;
64
+ margin-bottom: 15px;
65
+ color: #e0faff; /* Very light blue */
66
+ text-shadow: 0 0 10px #fff;
67
  }
68
 
69
+ .content-section h2 {
70
+ font-size: 1.8em;
71
+ margin-bottom: 15px;
72
+ color: #c0eaff;
73
+ }
74
+
75
  .content-section p {
76
  font-size: 1.1em;
77
  line-height: 1.6;
78
+ margin-bottom: 20px;
79
+ }
80
+
81
+ .content-section img.logo {
82
+ max-width: 150px;
83
+ margin-bottom: 20px;
84
+ }
85
+
86
+ /* Logo - Always Visible */
87
+ .site-logo {
88
+ position: fixed;
89
+ top: 20px;
90
+ left: 20px;
91
+ width: 100px;
92
+ height: auto;
93
+ z-index: 100;
94
+ filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.7));
95
  }
96
 
97
+ /* Mobile Navigation Hint */
98
+ .mobile-hint {
99
+ position: fixed;
100
  bottom: 20px;
101
  left: 50%;
102
  transform: translateX(-50%);
103
+ background-color: rgba(0, 0, 0, 0.6);
104
+ color: #fff;
105
+ padding: 8px 15px;
 
 
 
 
 
 
 
 
 
 
106
  border-radius: 20px;
107
+ font-size: 0.9em;
108
+ z-index: 50;
109
+ display: none; /* Hidden by default, shown via JS for touch devices */
110
+ animation: pulse 2s infinite;
111
+ }
112
 
113
+ @keyframes pulse {
114
+ 0% { opacity: 1; }
115
+ 50% { opacity: 0.7; }
116
+ 100% { opacity: 1; }
117
+ }
118
 
119
+ /* Loading Indicator */
120
+ #loader {
121
+ position: fixed;
122
+ top: 0;
123
+ left: 0;
124
+ width: 100%;
125
+ height: 100%;
126
+ background-color: #051d30;
127
+ display: flex;
128
+ justify-content: center;
129
+ align-items: center;
130
+ z-index: 200;
131
+ color: #fff;
132
+ font-size: 1.5em;
133
+ }
134
 
135
  </style>
 
 
 
 
 
 
136
  </head>
137
  <body>
138
 
139
+ <!-- Always visible Logo -->
140
+ <!-- Replace 'arctic_logo.png' with a Base64 encoded logo or a link if absolutely necessary -->
141
+ <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGRlZnM+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9ImdSYSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNlMGZhZmYiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjYWRkOGU2Ii8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KICAKICA8IS0tIEJhY2tncm91bmQgQ2lyY2xlIC0tPgogIDxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIHI9IjQ4IiBmaWxsPSJub25lIiBzdHJva2U9InVybCgjZ1JhKSIgc3Ryb2tlLXdpZHRoPSIzIiAvPgogIAogIDwhLS0gTW91bnRhaW4gU2hhcGVzIC0tPgogIDxwb2x5Z29uIHBvaW50cz0iMjUsNzUgNTAsMzAgNzUsNzUiIGZpbGw9InVybCgjZ1JhKSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEiIC8+CiAgPHBvbHlnb24gcG9pbnRzPSIzNSw3NSA1NSw1MCA3MCw3NSIgZmlsbD0iIzE0MjgzOCIgb3BhY2l0eT0iMC44IiAvPgoKICA8IS0tIFdhdGVyIFJpcHBsZXMgLS0+CiAgPHBhdGggZD0iTTIwLDgwIEMzMCw3NSw0MCw4NSw1MCw4MCIgc3Ryb2tlPSIjYWRkOGU2IiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiIG9wYWNpdHk9IjAuNyIvPgogIDxwYXRoIGQ9Ik01MCw4MyBDNjAsNzgsNzAsODgsODAsODMiIHN0cm9rZT0iI2FkZDhlNiIgc3Ryb2tlLXdpZHRoPSIyIiBmaWxsPSJub25lIiBvcGFjaXR5PSIwLjciLz4KCiAgPCEtLSBUZXh0ICJBIiAtLT4KICA8dGV4dCB4PSI1MCUiIHk9IjY1JSIgZG9taW5hbnQtYmFzZWxpbmU9Im1pZGRsZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IlNlcmlmLCBTYW5zLXNlcmlmIiBmb250LXNpemU9IjM1IiBmaWxsPSIjZmZmIiBmb250LXdlaWdodD0iYm9sZCI+CiAgICBBCiAgPC90ZXh0Pgo8L3N2Zz4K" alt="Arctic Logo" class="site-logo">
142
+
143
+ <!-- 3D Background Canvas -->
144
+ <canvas id="bg"></canvas>
145
 
146
+ <!-- Loading Indicator -->
147
+ <div id="loader">Loading Experience...</div>
148
+
149
+ <!-- Content Sections (Initially Hidden) -->
150
+ <div id="section-intro" class="content-section">
151
+ <!-- You can add a logo image here too -->
152
+ <!-- <img src="arctic_logo_white.png" alt="Arctic Logo" class="logo"> -->
153
+ <h1>Arctic</h1>
154
+ <p>Experience the Purest Water on Earth.</p>
155
+ <p style="font-size: 0.9em; opacity: 0.8;">Scroll down or swipe up to explore.</p>
156
  </div>
157
 
158
  <div id="section-source" class="content-section">
159
+ <h2>Our Source</h2>
160
+ <p>Sourced directly from pristine Arctic glaciers, untouched by man. Filtered naturally through ancient ice, ensuring unparalleled purity and a crisp, refreshing taste.</p>
161
  </div>
162
 
163
+ <div id="section-product" class="content-section">
164
+ <h2>The Water</h2>
165
+ <p>Perfectly balanced minerals provide a uniquely smooth taste. Available in Still and Sparkling. Choose Arctic for ultimate hydration and refreshment.</p>
166
+ <!-- Add bottle images here if possible (Base64 or linked) -->
167
  </div>
168
 
169
+ <div id="section-sustainability" class="content-section">
170
+ <h2>Sustainability</h2>
171
+ <p>Committed to preserving the Arctic. We use sustainable extraction methods and recyclable packaging to protect the environment that gives us our precious resource.</p>
 
172
  </div>
173
 
174
  <div id="section-contact" class="content-section">
175
+ <h2>Contact Us</h2>
176
+ <p>Learn more about Arctic or discuss partnership opportunities. Reach out to our team today.</p>
177
+ <p>Email: [email protected]</p>
178
  </div>
179
 
180
+ <!-- Mobile Navigation Hint -->
181
+ <div class="mobile-hint">Swipe Up or Down to Navigate</div>
182
+
183
+ <!-- Three.js Library (CDN) -->
184
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
185
 
186
+ <!-- Main Application Logic -->
187
  <script>
188
+ document.addEventListener('DOMContentLoaded', () => {
189
+ const loader = document.getElementById('loader');
190
+
191
+ // --- Configuration ---
192
+ const scrollMultiplier = 0.005; // How much scroll affects camera movement
193
+ const touchMultiplier = 0.008; // How much swipe affects camera movement
194
+ const cameraStartZ = 5;
195
+ const cameraEndZ = -45; // How "far" the user can scroll
196
+ const lerpFactor = 0.08; // Smoothing factor for camera movement (lower = smoother)
197
+ const fogNear = 1;
198
+ const fogFar = 60;
199
+ const fogColor = 0x051d30; // Match background
200
+
201
+ const contentSections = [
202
+ { id: 'section-intro', zStart: 5, zEnd: 0 },
203
+ { id: 'section-source', zStart: -5, zEnd: -15 },
204
+ { id: 'section-product', zStart: -20, zEnd: -30 },
205
+ { id: 'section-sustainability', zStart: -35, zEnd: -42 },
206
+ { id: 'section-contact', zStart: -44, zEnd: -50 } // A bit beyond camera end
207
+ ];
208
+
209
+ // --- Basic Scene Setup ---
210
+ let scene, camera, renderer;
211
+ let targetZ = cameraStartZ; // Target Z position for smooth animation
212
+ let currentZ = cameraStartZ; // Actual current Z position
213
+ let waterPlane, icebergs = [], particles;
214
+
215
+ // --- Mobile Touch Handling ---
216
+ let touchStartY = 0;
217
+ let isTouching = false;
218
+ const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
219
+
220
+ function init() {
221
+ // Scene
222
+ scene = new THREE.Scene();
223
+ scene.fog = new THREE.Fog(fogColor, fogNear, fogFar);
224
+ scene.background = new THREE.Color(fogColor); // Set background color
225
+
226
+ // Camera
227
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
228
+ camera.position.set(0, 1, cameraStartZ); // Start slightly above water level
229
+ camera.rotation.x = -0.1; // Slight downward look
230
+
231
+ // Renderer
232
+ const canvas = document.getElementById('bg');
233
+ renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
234
+ renderer.setSize(window.innerWidth, window.innerHeight);
235
+ renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // Improve performance on high-res screens
236
+
237
+ // --- Lighting ---
238
+ const ambientLight = new THREE.AmbientLight(0xadeeff, 0.6); // Soft blue ambient light
239
+ scene.add(ambientLight);
240
+
241
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); // Simulates sun/moon
242
+ directionalLight.position.set(5, 10, 7);
243
+ scene.add(directionalLight);
244
+
245
+ // --- Create Scene Elements ---
246
+ createWater();
247
+ createIcebergs();
248
+ createParticles(); // Add subtle particles
249
+
250
+ // --- Event Listeners ---
251
+ window.addEventListener('resize', onWindowResize);
252
+ window.addEventListener('wheel', onScroll, { passive: false });
253
+
254
+ if (isMobile) {
255
+ document.querySelector('.mobile-hint').style.display = 'block';
256
+ window.addEventListener('touchstart', onTouchStart, { passive: false });
257
+ window.addEventListener('touchmove', onTouchMove, { passive: false });
258
+ window.addEventListener('touchend', onTouchEnd);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  }
 
 
260
 
261
+ // Hide loader and start animation
262
+ loader.style.display = 'none';
263
+ animate();
 
264
  }
265
 
266
+ function createWater() {
267
+ // Simple plane for water surface
268
+ const waterGeometry = new THREE.PlaneGeometry(200, 200, 50, 50); // More segments for potential displacement later
269
+ // Material that reacts to light, looks somewhat 'wet'
270
+ const waterMaterial = new THREE.MeshStandardMaterial({
271
+ color: 0x60a0d0, // Blueish color
272
+ transparent: true,
273
+ opacity: 0.85,
274
+ roughness: 0.2, // Make it a bit shiny
275
+ metalness: 0.1,
276
+ side: THREE.DoubleSide,
277
+ // wireframe: true // For debugging
278
+ });
279
+ waterPlane = new THREE.Mesh(waterGeometry, waterMaterial);
280
+ waterPlane.rotation.x = -Math.PI / 2; // Lay flat
281
+ waterPlane.position.y = -1; // Position slightly below camera origin
282
+ scene.add(waterPlane);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  }
 
284
 
285
+ function createIcebergs() {
286
+ const icebergMaterial = new THREE.MeshStandardMaterial({
287
+ color: 0xe0faff, // Very light blue/white
288
+ roughness: 0.4,
289
+ metalness: 0.0
290
+ });
291
+
292
+ const numIcebergs = 40;
293
+ for (let i = 0; i < numIcebergs; i++) {
294
+ const sizeX = Math.random() * 3 + 1;
295
+ const sizeY = Math.random() * 5 + 2; // Taller
296
+ const sizeZ = Math.random() * 3 + 1;
297
+ const geometry = new THREE.BoxGeometry(sizeX, sizeY, sizeZ);
298
+
299
+ // Simple deformation
300
+ const positionAttribute = geometry.getAttribute('position');
301
+ for (let j = 0; j < positionAttribute.count; j++) {
302
+ const y = positionAttribute.getY(j);
303
+ const modifier = (y + sizeY / 2) / sizeY; // Modify more towards the top
304
+ positionAttribute.setX(j, positionAttribute.getX(j) + (Math.random() - 0.5) * 0.5 * modifier);
305
+ positionAttribute.setZ(j, positionAttribute.getZ(j) + (Math.random() - 0.5) * 0.5 * modifier);
306
+ if (y > 0) { // Taper top slightly
307
+ positionAttribute.setX(j, positionAttribute.getX(j) * (1 - (y / sizeY)*0.4) );
308
+ positionAttribute.setZ(j, positionAttribute.getZ(j) * (1 - (y / sizeY)*0.4) );
309
+ }
310
+ }
311
+ geometry.computeVertexNormals(); // Recalculate normals after deformation
312
+
313
+ const iceberg = new THREE.Mesh(geometry, icebergMaterial);
314
+
315
+ const angle = Math.random() * Math.PI * 2;
316
+ const distance = Math.random() * 30 + 10; // Distance from center path
317
+ const x = Math.cos(angle) * distance;
318
+ const z = Math.sin(angle) * distance - (i * (Math.abs(cameraEndZ - cameraStartZ)) / numIcebergs) - 15; // Spread along Z path
319
+ const y = -1 + sizeY / 2 - Math.random() * 0.5; // Base slightly below water
320
+
321
+ iceberg.position.set(x, y, z);
322
+ iceberg.rotation.y = Math.random() * Math.PI * 2;
323
+ iceberg.rotation.x = (Math.random() - 0.5) * 0.1;
324
+ iceberg.rotation.z = (Math.random() - 0.5) * 0.1;
325
+
326
+ scene.add(iceberg);
327
+ icebergs.push(iceberg);
328
+ }
329
+ }
330
+
331
+ function createParticles() {
332
+ const particleCount = 5000;
333
+ const positions = new Float32Array(particleCount * 3);
334
+ const colors = new Float32Array(particleCount * 3);
335
+ const color = new THREE.Color();
336
+
337
+ for (let i = 0; i < particleCount; i++) {
338
+ const i3 = i * 3;
339
+ positions[i3] = (Math.random() - 0.5) * 80; // Spread wider
340
+ positions[i3 + 1] = (Math.random() - 0.5) * 30 + 5; // Y range
341
+ positions[i3 + 2] = (Math.random() - 0.5) * Math.abs(cameraEndZ - cameraStartZ) * 1.5 + (cameraEndZ/2); // Z range along path
342
+
343
+ // Color based on depth (optional)
344
+ // const depthMix = Math.max(0, Math.min(1, (positions[i3+2] - cameraEndZ) / (cameraStartZ - cameraEndZ)));
345
+ color.setHSL(0.6, 0.9, Math.random() * 0.5 + 0.4); // Shades of blue/cyan
346
+
347
+ colors[i3] = color.r;
348
+ colors[i3 + 1] = color.g;
349
+ colors[i3 + 2] = color.b;
350
+ }
351
 
352
+ const geometry = new THREE.BufferGeometry();
353
+ geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
354
+ geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
355
+
356
+ const material = new THREE.PointsMaterial({
357
+ size: 0.08,
358
+ vertexColors: true,
359
+ transparent: true,
360
+ opacity: 0.7,
361
+ blending: THREE.AdditiveBlending, // Looks nice for particles
362
+ sizeAttenuation: true, // Smaller further away
363
+ depthWrite: false // Prevent particles obscuring closer objects harshly
364
+ });
365
+
366
+ particles = new THREE.Points(geometry, material);
367
+ scene.add(particles);
368
+ }
369
+
370
+ // --- Event Handlers ---
371
+ function onWindowResize() {
372
+ camera.aspect = window.innerWidth / window.innerHeight;
373
+ camera.updateProjectionMatrix();
374
+ renderer.setSize(window.innerWidth, window.innerHeight);
375
+ renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
376
+ }
377
+
378
+ function onScroll(event) {
379
+ event.preventDefault(); // Prevent default page scroll
380
+ const scrollDelta = event.deltaY;
381
+ targetZ -= scrollDelta * scrollMultiplier;
382
+ // Clamp target Z within bounds
383
+ targetZ = Math.max(cameraEndZ, Math.min(cameraStartZ, targetZ));
384
+ }
385
+
386
+ function onTouchStart(event) {
387
+ // event.preventDefault(); // Only prevent if vertical scroll is intended
388
+ if (event.touches.length === 1) {
389
+ isTouching = true;
390
+ touchStartY = event.touches[0].clientY;
391
+ }
 
 
 
392
  }
 
393
 
394
+ function onTouchMove(event) {
395
+ if (!isTouching || event.touches.length !== 1) return;
396
+ event.preventDefault(); // Prevent scrolling page while swiping on canvas
397
+ const touchCurrentY = event.touches[0].clientY;
398
+ const deltaY = touchCurrentY - touchStartY;
399
+ targetZ += deltaY * touchMultiplier; // Inverted compared to scroll
400
+ targetZ = Math.max(cameraEndZ, Math.min(cameraStartZ, targetZ));
401
+ touchStartY = touchCurrentY; // Update start position for continuous swipe
402
+ }
403
 
404
+ function onTouchEnd() {
405
+ isTouching = false;
406
+ }
407
+
408
+
409
+ // --- Content Visibility Logic ---
410
+ function updateContentVisibility() {
411
+ contentSections.forEach(section => {
412
+ const element = document.getElementById(section.id);
413
+ if (!element) return;
414
+
415
+ // Check if the camera's *current* Z is within the section's range
416
+ if (currentZ < section.zStart && currentZ > section.zEnd) {
417
+ element.classList.add('visible');
418
+ } else {
419
+ element.classList.remove('visible');
420
+ }
421
+ });
422
+ }
423
+
424
+
425
+ // --- Animation Loop ---
426
+ function animate() {
427
+ requestAnimationFrame(animate);
428
+
429
+ const deltaTime = 0.016; // Assume ~60fps for simplicity, or use THREE.Clock
430
 
431
+ // Smooth camera movement using lerp
432
+ currentZ = THREE.MathUtils.lerp(currentZ, targetZ, lerpFactor);
433
+ camera.position.z = currentZ;
434
+
435
+ // Subtle water movement (optional, very basic)
436
+ // waterPlane.material.displacementScale = 0.1; // Requires displacement map
437
+ // waterPlane.material.needsUpdate = true; // If material props change
438
+
439
+ // Subtle particle movement (optional)
440
+ if (particles) {
441
+ particles.rotation.y += 0.0005; // Slow rotation
442
+ // You could also update individual particle positions here for flow effect (more complex)
443
+ }
444
+
445
+ // Update which content section is visible
446
+ updateContentVisibility();
447
+
448
+ renderer.render(scene, camera);
449
+ }
450
+
451
+ // --- Start ---
452
+ init();
453
+ });
454
  </script>
455
 
456
  </body>