В этой статье мы рассмотрим, как создать эффект волн с помощью Canvas2D, имитируя 3D-окружение океана. Вы можете использовать этот эффект для создания красивых и динамичных фонов на вашем сайте или в приложениях. Демонстрацию можно посмотреть тут ДЕМО
HTML и CSS
Начнем с создания HTML-страницы и добавления элемента <canvas>
в нее. Мы также установим стили для канвы, чтобы она занимала всю доступную ширину и высоту.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wave Effect</title>
<style>
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas></canvas>
</body>
</html>
JavaScript
Теперь перейдем к JavaScript-части, где мы создадим эффект волн.
Инициализация контекста
Сначала мы инициализируем контекст Canvas и создаем дополнительный контекст для пост-обработки.
// Init Context
let c = document.createElement('canvas').getContext('2d');
let postctx = document.body.appendChild(document.createElement('canvas')).getContext('2d');
let canvas = c.canvas;
Свойства эффекта
Здесь мы определяем свойства эффекта, такие как количество вершин, размер вершины, ширина и высота океана, размер сетки и другие параметры.
// Effect Properties
let vertexCount = 7000;
let vertexSize = 3;
let oceanWidth = 204;
let oceanHeight = -80;
let gridSize = 32;
let waveSize = 16;
let perspective = 100;
Общие переменные
Вычисляем глубину и инициализируем переменную frame
для отслеживания времени.
// Common variables
let depth = (vertexCount / oceanWidth * gridSize);
let frame = 0;
let { sin, cos, tan, PI } = Math;
Генерация вершин
Создаем массив vertices
, в котором будут храниться координаты вершин.
// Generate vertices
let vertices = [];
for (let i = 0; i < vertexCount; i++) {
let x = i % oceanWidth;
let y = 0;
let z = i / oceanWidth >> 0;
let offset = oceanWidth / 2;
vertices.push([(-offset + x) * gridSize, y * gridSize, z * gridSize]);
}
Цикл рендеринга
Функция loop
будет вызываться на каждом кадре для обновления эффекта.
// Render loop
let oldTimeStamp = performance.now();
function loop(timeStamp) {
let rad = sin(frame / 100) * PI / 20;
let rad2 = sin(frame / 50) * PI / 10;
const dt = (timeStamp - oldTimeStamp) / 1000;
oldTimeStamp = timeStamp;
frame += dt * 50;
if (postctx.canvas.width !== postctx.canvas.offsetWidth || postctx.canvas.height !== postctx.canvas.offsetHeight) {
postctx.canvas.width = canvas.width = postctx.canvas.offsetWidth;
postctx.canvas.height = canvas.height = postctx.canvas.offsetHeight;
}
// ... (код рендеринга)
}
loop(performance.now());
Рендеринг
Здесь мы рисуем точки и применяем преобразования для создания эффекта волн.
c.fillStyle = `hsl(200deg, 100%, 2%)`;
c.fillRect(0, 0, canvas.width, canvas.height);
c.save();
c.translate(canvas.width / 2, canvas.height / 2);
c.beginPath();
vertices.forEach((vertex, i) => {
let ni = i + oceanWidth;
let x = vertex[0] - frame % (gridSize * 2);
let z = vertex[2] - frame * 2 % gridSize + (i % 2 === 0 ? gridSize / 2 : 0);
let wave = cos(frame / 45 + x / 50) - sin(frame / 20 + z / 50) + sin(frame / 30 + z * x / 10000);
let y = vertex[1] + wave * waveSize;
let a = Math.max(0, 1 - (Math.sqrt(x ** 2 + z ** 2)) / depth);
let tx, ty, tz;
y -= oceanHeight;
// ... (преобразования)
c.globalAlpha = a;
c.fillStyle = `hsl(${180 + wave * 20}deg, 100%, 50%)`;
c.fillRect(x - a * vertexSize / 2, y - a * vertexSize / 2, a * vertexSize, a * vertexSize);
c.globalAlpha = 1;
});
c.restore();
Пост-обработка
Применяем пост-обработку для создания эффекта размытия и увеличения яркости.
postctx.drawImage(canvas, 0, 0);
postctx.globalCompositeOperation = "screen";
postctx.filter = 'blur(16px)';
postctx.drawImage(canvas, 0, 0);
postctx.filter = 'blur(0)';
postctx.globalCompositeOperation = "source-over";
Заключение
В этой статье мы рассмотрели, как создать эффект волн из точек с помощью Canvas2D. Вы можете использовать этот эффект для создания красивых и динамичных фонов на вашем сайте или в приложениях. Экспериментируйте с различными параметрами, чтобы добиться желаемого результата.