Создание волн из точек с помощью Canvas

В этой статье мы рассмотрим, как создать эффект волн с помощью 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. Вы можете использовать этот эффект для создания красивых и динамичных фонов на вашем сайте или в приложениях. Экспериментируйте с различными параметрами, чтобы добиться желаемого результата.