Движущиеся ноды со связями на JavaScript Canvas

Представляю вашему вниманию очередное мое творение, которое достаточно долгое время пролежало «под сукном».

nodes

Это движущиеся круглые объекты со связями, которые могут появляться по мере сближения объектов и исчезать по мере их удаления. Написано все на JavaScript Canvas и на не очень сильных компьютерах может подтормаживать ввиду особенностей реализации.

Хочу честно признаться: идея не моя. Где-то в сети (уже не помню где) я видел что-то похожее на SVG.

Общий принцип
Общий принцип прост. Внизу экрана (за его пределами) появляются круглые объекты случайного радиуса и со случайной скоростью движутся вверх. Если между двумя объектами расстояние сокращается до определенной величины (теорему Пифагора помним, да?), то между ними возникает призрачная, едва заметная связь, которая становится все более явной по мере сближения объектов. Наоборот, при удалении объектов друг от друга эта связь ослабевает и в конце концов исчезает. Сами объекты исчезают чуть выше верхней границы экрана, чтобы вновь появиться снизу с новыми размером и скоростью.

HTML и CSS
На html-странице нам понадобится canvas-объект:

Стили для страницы:

body
{
margin: 0px;
padding: 0px;
}

#canvas
{
position: absolute;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
}

JavaScript
Как можно видеть из принципа, нам понадобятся два типа: «Объект/Нода» и «Связь».

Реализуем их:

function Node(x, y) {
this.x = (x === undefined) ? Math.floor(Math.random() * canva.width) : x;
// ниже нижней границы
this.y = (y === undefined) ? Math.floor(Math.random() * (canva.height + 2 * MAX_DIST) — MAX_DIST) : y;
this.r = Math.floor(Math.random() * 5 + 5); // радиус не меньше 5 и не больше 10.
this.v = Math.random() * 1.3 + 0.3; // скорость от 0.3 до 1.6

// отрисовка для текущих координат
this.render = function() {
// «свечение» вокруг объекта
ctx.beginPath();
ctx.fillStyle = ‘rgba(255, 255, 255, 0.3)’;
ctx.arc(this.x, this.y, this.r * 2, 0, 2 * Math.PI, true);
ctx.fill();

// сам объект
ctx.beginPath();
ctx.fillStyle = ‘rgba(255, 255, 255, 0.8)’;
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI, true);
ctx.fill();
}
}

function Link(x1, y1, x2, y2, d) {
// координаты первого объекта
this.x1 = x1;
this.y1 = y1;
// координаты второго объекта
this.x2 = x2;
this.y2 = y2;
// расстояние между ними
this.distance = d;

// отрисовка для текущих координат
this.render = function() {
// степень прозрачности
var transp = 1 — (this.distance / (MAX_DIST / 100)) * 0.01;
// отрисовка линии
ctx.beginPath();
ctx.strokeStyle = ‘rgba(255, 255, 255, ‘ + transp + ‘)’;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
}

MAX_DIST — то самое расстояние, на котором появляется связь.
Понадобятся несколько вспомогательных функций:
// очистка канвы

function clear() {
ctx.clearRect(0, 0, canva.width, canva.height)
ctx.fillStyle = ‘rgb(36, 36, 36)’;
ctx.fillRect(0, 0, canva.width, canva.height);
}

// расстояние между двумя точками по теореме Пифагора

function distance(x1, y1, x2, y2) {
return Math.floor(Math.sqrt((x1 — x2) * (x1 — x2) + (y1 — y2) * (y1 — y2)));

}
Начальная инициализация:

var canva = document.getElementById(«canvas»);
canva.width = window.innerWidth;
canva.height = window.innerHeight;
var ctx = canva.getContext(«2d»);

var nodes = []; // объекты
var links = []; // связи

var N = 25; // число объектов

// инициализация объектов
for (var i = 0; i < N; i++) { nodes[nodes.length] = new Node(); // объект создается внизу и со случайной абсциссой. } // первый просчет связей calcLinks(); // запуск анимации setInterval(draw, 50) calcLinks — вычисление связей function calcLinks() { // для каждой пары объектов for (var i = 0; i < N; i++) { for (var k = i + 1; k < N; k++) { var dist = distance(nodes[i].x, nodes[i].y, nodes[k].x, nodes[k].y); if (dist <= MAX_DIST) // новая связь links[links.length] = new Link(nodes[i].x, nodes[i].y, nodes[k].x, nodes[k].y, dist); } } } draw — вывод на экран одного кадра, коих за секунду сменится 20 штук. function draw() { clear(); // вывод связей for (var i = 0; i < links.length; i++) { links[i].render(); } // уже поверх линий-связей рисуются объекты for (var i = 0; i < nodes.length; i++) { nodes[i].render(); nodes[i].y -= nodes[i].v; if (nodes[i].y < -1 * MAX_DIST) { // объект достиг "потолка" и пересоздается снизу nodes[i] = new Node(); // очень полагаюсь на стандартный сборщик мусора nodes[i].y = canva.height + MAX_DIST; } } // пересчет связей links = []; calcLinks(); }

Результат вы могли увидеть по ссылке сверху, но я дам ее еще раз: демо «Nodes & Links».

Кое-что еще
Значение N можно увеличивать в разумных пределах в зависимости от ширины экрана пользователя.

Возможно, все это можно оптимизировать, играясь с сочетанием FPS (кадров в секунду) и скорости движения объектов.

Можно реализовать parallax-эффект. Предположим, что все объекты одинакового размера. Тогда те, что мы делаем меньшего размера, будут как бы находится в отдалении, а потому и двигаться с меньшей скорость. Т.е. чем меньше объект, тем меньше его скорость.

Читайте так же:
Оставить комментарий

Последние публикации