Aleksmorshen commited on
Commit
dbc4d00
·
verified ·
1 Parent(s): fd12e08

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +112 -450
index.html CHANGED
@@ -1,456 +1,118 @@
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
- /* --- Базовые стили --- */
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- }
14
-
15
- html {
16
- scroll-behavior: smooth; /* Плавный скролл по якорям (если нужны) */
17
- }
18
-
19
- body {
20
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
21
- color: #fff;
22
- background-color: #001f3f; /* Темно-синий фон на случай, если 3D не загрузится */
23
- overflow-x: hidden; /* Запрещаем горизонтальный скролл */
24
- /* Важно: Высота body должна быть больше viewport, чтобы был скролл */
25
- /* Это достигается за счет высоты контентных секций */
26
- }
27
-
28
- /* --- Контейнер для 3D сцены --- */
29
- #canvas-container {
30
- position: fixed;
31
- top: 0;
32
- left: 0;
33
- width: 100%;
34
- height: 100%;
35
- z-index: -1; /* Помещаем canvas позади контента */
36
- overflow: hidden; /* Обрезаем все, что выходит за пределы */
37
- }
38
-
39
- canvas {
40
- display: block; /* Убираем лишние отступы */
41
- }
42
-
43
- /* --- Контейнер для контента --- */
44
- #content-container {
45
- position: relative; /* Чтобы z-index работал */
46
- z-index: 1;
47
- width: 100%;
48
- max-width: 900px; /* Ограничиваем ширину контента на десктопах */
49
- margin: 0 auto; /* Центрируем контент */
50
- padding: 0 20px; /* Отступы по бокам */
51
- }
52
-
53
- /* --- Секции контента --- */
54
- .content-section {
55
- min-height: 100vh; /* Каждая секция занимает минимум высоту экрана */
56
- padding: 100px 20px 50px; /* Отступы внутри секции */
57
- display: flex;
58
- flex-direction: column;
59
- justify-content: center;
60
- align-items: center;
61
- text-align: center;
62
- /* Полупрозрачный фон для лучшей читаемости */
63
- background-color: rgba(10, 30, 60, 0.6);
64
- margin-bottom: 5vh; /* Небольшой отступ между секциями */
65
- border-radius: 15px;
66
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
67
- transition: background-color 0.5s ease;
68
- }
69
-
70
- .content-section:last-child {
71
- margin-bottom: 0;
72
- }
73
-
74
- /* --- Стилизация текста и элементов --- */
75
- h1, h2, h3 {
76
- margin-bottom: 20px;
77
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
78
- }
79
-
80
- h1 {
81
- font-size: 2.8em;
82
- font-weight: bold;
83
- color: #aee1f9; /* Светло-голубой */
84
- }
85
-
86
- h2 {
87
- font-size: 2em;
88
- color: #e0f7fa; /* Очень светло-голубой */
89
- }
90
-
91
- p {
92
- font-size: 1.1em;
93
- line-height: 1.6;
94
- margin-bottom: 15px;
95
- max-width: 600px; /* Ограничиваем ширину параграфов */
96
- }
97
-
98
- .logo {
99
- max-width: 200px;
100
- margin-bottom: 30px;
101
- /* filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.5)); */
102
- /* Замените src на реальный логотип (лучше SVG или PNG с прозрачностью) */
103
- }
104
-
105
- .cta-button {
106
- display: inline-block;
107
- padding: 12px 25px;
108
- background-color: #00a8ff; /* Яркий голубой */
109
- color: #fff;
110
- border: none;
111
- border-radius: 25px;
112
- font-size: 1.1em;
113
- text-decoration: none;
114
- font-weight: bold;
115
- cursor: pointer;
116
- transition: background-color 0.3s ease, transform 0.2s ease;
117
- box-shadow: 0 4px 8px rgba(0, 168, 255, 0.4);
118
- }
119
-
120
- .cta-button:hover {
121
- background-color: #007bb5; /* Темнее при наведении */
122
- transform: translateY(-2px);
123
- }
124
-
125
- /* Адаптивность для меньших экранов */
126
- @media (max-width: 768px) {
127
- h1 { font-size: 2.2em; }
128
- h2 { font-size: 1.8em; }
129
- p { font-size: 1em; }
130
- .content-section { padding: 80px 15px 40px; }
131
- .logo { max-width: 150px; }
132
- }
133
-
134
- @media (max-width: 480px) {
135
- h1 { font-size: 1.8em; }
136
- h2 { font-size: 1.5em; }
137
- p { font-size: 0.9em; }
138
- .content-section { padding: 60px 10px 30px; min-height: 90vh; }
139
- .logo { max-width: 120px; }
140
- .cta-button { font-size: 1em; padding: 10px 20px; }
141
- }
142
-
143
- /* --- Стили для "продуктов" (просто пример) --- */
144
- .product-gallery {
145
- display: flex;
146
- flex-wrap: wrap;
147
- justify-content: center;
148
- gap: 20px;
149
- margin-top: 20px;
150
- }
151
-
152
- .product-item {
153
- background-color: rgba(255, 255, 255, 0.1);
154
- padding: 15px;
155
- border-radius: 10px;
156
- width: 150px; /* Фиксированная ширина для примера */
157
- text-align: center;
158
- }
159
-
160
- .product-item img {
161
- max-width: 80px;
162
- height: auto;
163
- margin-bottom: 10px;
164
- }
165
-
166
- .product-item h3 {
167
- font-size: 1em;
168
- margin-bottom: 5px;
169
- color: #fff;
170
- }
171
-
172
- /* --- Простой индикатор загрузки --- */
173
- #loader {
174
- position: fixed;
175
- top: 0; left: 0;
176
- width: 100%; height: 100%;
177
- background-color: #001f3f;
178
- display: flex;
179
- justify-content: center;
180
- align-items: center;
181
- z-index: 1000;
182
- color: #fff;
183
- font-size: 1.5em;
184
- }
185
-
186
- </style>
187
  </head>
188
  <body>
189
-
190
- <!-- Индикатор загрузки -->
191
- <div id="loader">Загрузка 3D сцены...</div>
192
-
193
- <!-- Контейнер для Canvas -->
194
- <div id="canvas-container"></div>
195
-
196
- <!-- Основной контент сайта -->
197
- <div id="content-container">
198
-
199
- <!-- Секция 1: Главный экран -->
200
- <section id="intro" class="content-section">
201
- <!-- Замените на ваш логотип -->
202
- <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNDUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2FlZTFmOSIgc3Ryb2tlLXdpZHRoPSI1Ii8+CiAgPHBhdGggZD0iTTMwIDQwIFE1MCAxNSA3MCA0MCIgc3Ryb2tlPSIjMDBlZWY4IiBzdHJva2Utd2lkdGg9IjUiIGZpbGw9Im5vbmUiLz4KICA8cGF0aCBkPSJNMzUgNjAgUTUwIDg1IDY1IDYwIiBzdHJva2U9IiMxN2RhZjIiIHN0cm9rZS13aWR0aD0iNCIgZmlsbD0ibm9uZSIvPgogIDx0ZXh0IHg9IjUwIiB5PSI3NSIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGZpbGw9IiNmZmYiIHRleHQtYW5jaG9yPSJtaWRkbGUiPkFjdGljPC90ZXh0Pgo8L3N2Zz4K" alt="Arctic Logo Placeholder" class="logo">
203
- <h1>Arctic</h1>
204
- <h2>Чистота северных глубин в каждой капле</h2>
205
- <p>Откройте для себя вкус настоящей природной свежести. Вода "Arctic" добывается из артезианских источников, защищенных вечной мерзлотой.</p>
206
- <a href="#about" class="cta-button">Узнать больше</a>
207
- </section>
208
-
209
- <!-- Секция 2: О нас -->
210
- <section id="about" class="content-section">
211
- <h2>Источник чистоты</h2>
212
- <p>Наша вода проходит естественную фильтрацию через слои древних минеральных пород, обогащаясь полезными элементами и сохраняя свою первозданную структуру. Мы бережно доставляем ее вам, сохраняя все уникальные свойства.</p>
213
- <p>Технологии XXI века и уважение к природе позволяют нам гарантировать высочайшее качество "Arctic".</p>
214
- </section>
215
-
216
- <!-- Секция 3: Продукция -->
217
- <section id="products" class="content-section">
218
- <h2>Наша линейка</h2>
219
- <p>Выберите свой формат "Arctic" для любого случая.</p>
220
- <div class="product-gallery">
221
- <div class="product-item">
222
- <!-- Замените на фото продукта -->
223
- <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgODAgMTIwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxyZWN0IHg9IjEwIiB5PSIyMCIgd2lkdGg9IjYwIiBoZWlnaHQ9Ijk1IiByeD0iMTAiIGZpbGw9IiNhZWUxZjkiIG9wYWNpdHk9IjAuNyIvPgogIDxyZWN0IHg9IjIwIiB5PSI1IiB3aWR0aD0iNDAiIGhlaWdodD0iMjAiIHJ4PSI1IiBmaWxsPSIjZmZmIi8+CiAgPHJlY3QgeD0iMTUiIHk9IjI1IiB3aWR0aD0iNTAiIGhlaWdodD0iODAiIGZpbGw9IiNmZmYiIG9wYWNpdHk9IjAuMyIvPgo8L3N2Zz4K" alt="Бутылка 0.5л">
224
- <h3>0.5л Негазированная</h3>
225
- </div>
226
- <div class="product-item">
227
- <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgODAgMTIwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxyZWN0IHg9IjEwIiB5PSIyMCIgd2lkdGg9IjYwIiBoZWlnaHQ9Ijk1IiByeD0iMTAiIGZpbGw9IiMzYzkzYjgiIG9wYWNpdHk9IjAuNyIvPgogIDxyZWN0IHg9IjIwIiB5PSI1IiB3aWR0aD0iNDAiIGhlaWdodD0iMjAiIHJ4PSI1IiBmaWxsPSIjZmZmIi8+CiAgPHJlY3QgeD0iMTUiIHk9IjI1IiB3aWR0aD0iNTAiIGhlaWdodD0iODAiIGZpbGw9IiNmZmYiIG9wYWNpdHk9IjAuMyIvPgo8L3N2Zz4K" alt="Бутылка 1.5л">
228
- <h3>1. Газированная</h3>
229
- </div>
230
- <div class="product-item">
231
- <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgODAgMTIwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxyZWN0IHg9IjEwIiB5PSIyMCIgd2lkdGg9IjYwIiBoZWlnaHQ9Ijk1IiByeD0iMTAiIGZpbGw9IiNiM2U1ZmMiIG9wYWNpdHk9IjAuNyIvPgogIDxyZWN0IHg9IjIwIiB5PSI1IiB3aWR0aD0iNDAiIGhlaWdodD0iMjAiIHJ4PSI1IiBmaWxsPSIjZmZmIi8+CiAgPHJlY3QgeD0iMTUiIHk9IjI1IiB3aWR0aD0iNTAiIGhlaWdodD0iODAiIGZpbGw9IiNmZmYiIG9wYWNpdHk9IjAuMyIvPgo8L3N2Zz4K" alt="Бутылка 5л">
232
- <h3>5л Негазированная (для дома и офиса)</h3>
233
- </div>
234
- </div>
235
- </section>
236
-
237
- <!-- Секция 4: Контакты -->
238
- <section id="contact" class="content-section">
239
- <h2>Связаться с нами</h2>
240
- <p>Есть вопросы или предложения? Мы всегда рады общению.</p>
241
- <p>Телефон: 8 (800) 123-45-67 (Звонок бесплатный)</p>
242
- <p>Email: [email protected]</p>
243
- <a href="mailto:info@arctic-water.example.com" class="cta-button">Написать нам</a>
244
- </section>
245
-
246
- </div>
247
-
248
- <!-- Подключение Three.js и аддонов -->
249
- <script type="importmap">
250
- {
251
- "imports": {
252
- "three": "https://unpkg.com/[email protected]/build/three.module.js",
253
- "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
254
- }
255
- }
256
- </script>
257
-
258
- <script type="module">
259
- import * as THREE from 'three';
260
- import { Water } from 'three/addons/objects/Water.js';
261
- // OrbitControls можно добавить для отладки навигации, но для финальной версии скролла он не нужен
262
- // import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
263
-
264
- let scene, camera, renderer, water, clock;
265
- let container;
266
- let scrollY = 0;
267
- let targetCameraZ = 15; // Начальная позиция камеры по Z
268
- const cameraPathLength = 50; // Общая длина "пути" камеры по Z
269
- const cameraBobbingAmplitude = 0.5; // Амплитуда покачивания камеры по Y
270
- const cameraBobbingFrequency = 0.5; // Частота покачивания
271
-
272
- const loaderElement = document.getElementById('loader');
273
-
274
- function init() {
275
- container = document.getElementById('canvas-container');
276
- clock = new THREE.Clock();
277
-
278
- // --- Сцена ---
279
- scene = new THREE.Scene();
280
- scene.fog = new THREE.FogExp2(0x0a1f30, 0.015); // Легкий туман для атмосферы
281
-
282
- // --- Камера ---
283
- camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
284
- camera.position.set(0, 5, targetCameraZ); // Начальная позиция чуть выше воды
285
- camera.lookAt(0, 2, 0); // Смотрим немного вниз к центру сцены
286
-
287
- // --- Рендерер ---
288
- renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false }); // alpha: false для непрозрачного фона
289
- renderer.setSize(window.innerWidth, window.innerHeight);
290
- renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // Ограничиваем pixelRatio для производительности
291
- renderer.toneMapping = THREE.ACESFilmicToneMapping; // Для более приятного вида
292
- container.appendChild(renderer.domElement);
293
-
294
- // --- Свет ---
295
- const ambientLight = new THREE.AmbientLight(0xcccccc, 0.8); // Мягкий общий свет
296
- scene.add(ambientLight);
297
-
298
- const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5); // Солнечный свет
299
- directionalLight.position.set(-50, 50, 20);
300
- // directionalLight.castShadow = true; // Тени могут сильно ударить по производительности, включать осторожно
301
- scene.add(directionalLight);
302
-
303
- // --- Вода ---
304
- const waterGeometry = new THREE.PlaneGeometry(1000, 1000); // Большой плейн для воды
305
-
306
- // Текстура для нормалей воды (создает эффект ряби)
307
- // Используем текстуру из примеров three.js
308
- const waterNormals = new THREE.TextureLoader().load(
309
- 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/waternormals.jpg',
310
- function ( texture ) {
311
- texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
312
- console.log("Water normals loaded successfully.");
313
- // Убираем лоадер после загрузки текстуры
314
- if (loaderElement) loaderElement.style.display = 'none';
315
- // Начинаем анимацию только после загрузки
316
- animate();
317
- },
318
- undefined, // onProgress callback (можно добавить)
319
- function ( err ) {
320
- console.error( 'An error happened loading the water texture:', err );
321
- // Если текстура не загрузилась, все равно убираем лоадер и пытаемся рисовать
322
- if (loaderElement) loaderElement.style.display = 'none';
323
- animate(); // Пытаемся запустить анимацию даже без текстуры
324
- }
325
- );
326
-
327
-
328
- water = new Water(
329
- waterGeometry,
330
- {
331
- textureWidth: 512,
332
- textureHeight: 512,
333
- waterNormals: waterNormals,
334
- sunDirection: new THREE.Vector3(-50, 50, 20), // Направление света для отражений/бликов
335
- sunColor: 0xffffff,
336
- waterColor: 0x003e5f, // Цвет воды (темно-сине-зеленый)
337
- distortionScale: 3.0, // Сила искажений ряби
338
- fog: scene.fog !== undefined
339
- }
340
- );
341
-
342
- water.rotation.x = - Math.PI / 2; // Поворачиваем плейн, чтобы он был горизонтальным
343
- water.position.y = 0; // Устанавливаем уровень воды
344
- scene.add(water);
345
-
346
- // --- Небо (простой вариант - куб с градиентом) ---
347
- const skyGeometry = new THREE.SphereGeometry(500, 32, 16); // Большая сфера
348
- const skyMaterial = new THREE.MeshBasicMaterial({
349
- color: 0x87ceeb, // Цвет неба
350
- side: THREE.BackSide, // Рендерим внутреннюю часть сферы
351
- fog: false // Небо не подвержено туману
352
- });
353
- // Можно усложнить, сделав градиентный шейдер или загрузив CubeTexture
354
- const sky = new THREE.Mesh(skyGeometry, skyMaterial);
355
- sky.position.y = -10; // Слегка опускаем, чтобы горизонт был виден
356
- scene.add(sky);
357
-
358
- // --- (Опционально) Добавление простых объектов "айсбергов" ---
359
- const icebergGeometry = new THREE.IcosahedronGeometry(5, 0); // Простая геометрия
360
- const icebergMaterial = new THREE.MeshStandardMaterial({
361
- color: 0xe0f7fa,
362
- roughness: 0.7,
363
- metalness: 0.1,
364
- flatShading: true // "Граненый" вид
365
- });
366
-
367
- for (let i = 0; i < 5; i++) {
368
- const iceberg = new THREE.Mesh(icebergGeometry, icebergMaterial);
369
- iceberg.position.set(
370
- (Math.random() - 0.5) * 150, // Разброс по X
371
- Math.random() * 1 - 0.5, // Немного над/под водой
372
- (Math.random() - 0.5) * 150 // Разброс по Z
373
- );
374
- iceberg.rotation.set(Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI);
375
- iceberg.scale.setScalar(Math.random() * 2 + 1); // Разный размер
376
- scene.add(iceberg);
377
- }
378
-
379
-
380
- // --- Обработчики событий ---
381
- window.addEventListener('resize', onWindowResize);
382
- window.addEventListener('scroll', onScroll);
383
-
384
- // Вызываем обработчик скролла один раз, чтобы установить начальную позицию камеры
385
- onScroll();
386
-
387
- console.log("Three.js scene initialized.");
388
- }
389
-
390
- function onWindowResize() {
391
- camera.aspect = window.innerWidth / window.innerHeight;
392
- camera.updateProjectionMatrix();
393
- renderer.setSize(window.innerWidth, window.innerHeight);
394
- renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
395
- }
396
-
397
- function onScroll() {
398
- // Нормализуем скролл (от 0 до 1)
399
- const scrollableHeight = document.documentElement.scrollHeight - window.innerHeight;
400
- // Предотвращаем деление на ноль, если контент не прокручивается
401
- const scrollProgress = scrollableHeight > 0 ? window.scrollY / scrollableHeight : 0;
402
-
403
- // Обновляем целевую позицию камеры по Z
404
- // Двигаемся от начальной точки `targetCameraZ` "вглубь" на `cameraPathLength`
405
- targetCameraZ = 15 - (scrollProgress * cameraPathLength);
406
-
407
- // Обновляем целевую позицию камеры по Y для эффекта покачивания
408
- // Используем время (или скролл) для синусоиды
409
- const time = clock.getElapsedTime(); // Можно использовать scrollProgress * N для зависимости от скролла
410
- const bobbingY = Math.sin(time * cameraBobbingFrequency * Math.PI * 2) * cameraBobbingAmplitude;
411
- const baseCameraY = 5; // Базовая высота камеры над водой
412
- // targetCameraY = baseCameraY + bobbingY; // Пока без покачивания, чтобы сфокусироваться на Z
413
- // camera.position.y = baseCameraY + bobbingY; // Обновляем Y с покачиванием
414
-
415
- // Плавное перемещение камеры к целевым значениям (не в onScroll, а в animate)
416
- }
417
-
418
- function animate() {
419
- requestAnimationFrame(animate);
420
-
421
- const delta = clock.getDelta();
422
- const time = clock.getElapsedTime();
423
-
424
- // Анимация воды
425
- if (water) {
426
- water.material.uniforms['time'].value += delta * 0.5; // Скорость анимации воды
427
- }
428
-
429
- // Плавное (интерполированное) движение камеры к целевым значениям
430
- const lerpFactor = 0.05; // Коэффициент плавности (меньше = плавнее, но медленнее)
431
- camera.position.z += (targetCameraZ - camera.position.z) * lerpFactor;
432
-
433
- // Добавляем покачивание на основе времени
434
- const bobbingY = Math.sin(time * cameraBobbingFrequency * Math.PI * 2) * cameraBobbingAmplitude;
435
- const targetCameraY = 5 + bobbingY; // Базовая высота + покачивание
436
- camera.position.y += (targetCameraY - camera.position.y) * lerpFactor;
437
-
438
-
439
- // Обновляем взгляд камеры (если нужно, чтобы она смотрела не прямо)
440
- // camera.lookAt(0, 2, camera.position.z - 10); // Смотрим немного вперед по пути
441
-
442
- renderer.render(scene, camera);
443
- }
444
-
445
- // Запускаем инициализацию
446
- // Ждем загрузки DOM перед инициализацией Three.js
447
- if (document.readyState === 'loading') {
448
- document.addEventListener('DOMContentLoaded', init);
449
- } else {
450
- init(); // DOM уже загружен
451
- }
452
-
453
- </script>
454
-
455
  </body>
456
  </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="en">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <title>Вода Симуляция</title>
6
+ <style>
7
+ html, body {
8
+ margin: 0;
9
+ padding: 0;
10
+ overflow: hidden;
11
+ background: #0e0f1b;
12
+ }
13
+ canvas {
14
+ display: block;
15
+ background: linear-gradient(#0f2027, #203a43, #2c5364);
16
+ }
17
+ </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  </head>
19
  <body>
20
+ <canvas id="water"></canvas>
21
+ <script>
22
+ const canvas = document.getElementById('water');
23
+ const ctx = canvas.getContext('2d');
24
+
25
+ let width = window.innerWidth;
26
+ let height = window.innerHeight;
27
+ canvas.width = width;
28
+ canvas.height = height;
29
+
30
+ const waveCount = 200;
31
+ const damping = 0.03;
32
+ const tension = 0.025;
33
+
34
+ const waves = [];
35
+ for (let i = 0; i < waveCount; i++) {
36
+ waves[i] = {
37
+ x: (i / (waveCount - 1)) * width,
38
+ y: height / 2,
39
+ vy: 0,
40
+ targetY: height / 2
41
+ };
42
+ }
43
+
44
+ function update() {
45
+ for (let i = 0; i < waveCount; i++) {
46
+ const point = waves[i];
47
+ const dy = point.y - point.targetY;
48
+ point.vy -= dy * tension;
49
+ point.vy *= 1 - damping;
50
+ point.y += point.vy;
51
+ }
52
+
53
+ // propagate waves
54
+ const leftDeltas = [], rightDeltas = [];
55
+ for (let j = 0; j < 8; j++) {
56
+ for (let i = 0; i < waveCount; i++) {
57
+ if (i > 0) {
58
+ leftDeltas[i] = 0.25 * (waves[i].y - waves[i - 1].y);
59
+ waves[i - 1].vy += leftDeltas[i];
60
+ }
61
+ if (i < waveCount - 1) {
62
+ rightDeltas[i] = 0.25 * (waves[i].y - waves[i + 1].y);
63
+ waves[i + 1].vy += rightDeltas[i];
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ function draw() {
70
+ ctx.clearRect(0, 0, width, height);
71
+
72
+ ctx.beginPath();
73
+ ctx.moveTo(0, height);
74
+ ctx.lineTo(waves[0].x, waves[0].y);
75
+ for (let i = 1; i < waveCount; i++) {
76
+ ctx.lineTo(waves[i].x, waves[i].y);
77
+ }
78
+ ctx.lineTo(width, height);
79
+ ctx.closePath();
80
+
81
+ const gradient = ctx.createLinearGradient(0, height / 2, 0, height);
82
+ gradient.addColorStop(0, '#4facfe');
83
+ gradient.addColorStop(1, '#00f2fe');
84
+
85
+ ctx.fillStyle = gradient;
86
+ ctx.fill();
87
+ }
88
+
89
+ function loop() {
90
+ update();
91
+ draw();
92
+ requestAnimationFrame(loop);
93
+ }
94
+
95
+ loop();
96
+
97
+ canvas.addEventListener('mousemove', (e) => {
98
+ const mouseX = e.clientX;
99
+ const index = Math.floor((mouseX / width) * waveCount);
100
+ if (index >= 0 && index < waveCount) {
101
+ waves[index].vy = -5;
102
+ }
103
+ });
104
+
105
+ window.addEventListener('resize', () => {
106
+ width = window.innerWidth;
107
+ height = window.innerHeight;
108
+ canvas.width = width;
109
+ canvas.height = height;
110
+ for (let i = 0; i < waveCount; i++) {
111
+ waves[i].x = (i / (waveCount - 1)) * width;
112
+ waves[i].y = height / 2;
113
+ waves[i].targetY = height / 2;
114
+ }
115
+ });
116
+ </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  </body>
118
  </html>