Transition from CodePen

This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2022-11-01 09:19:17 +10:00
parent 4144a13a81
commit 7cd23a51da

292
graph.js Normal file
View File

@ -0,0 +1,292 @@
"use strict";
/**
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
class graph {
// Оболочка (instanceof HTMLElement)
shell = document.getElementById("graph");
// Реестр узлов
nodes = new Set();
// Реестр соединений
connections = new Set();
// Класс узла
node = class node {
// Реестр входящих соединений
inputs = new Map();
// Реестр исходящих соединений
outputs = new Map();
// HTML-элемент
element;
// Параметры генерации
#diameter = 100;
#increase = 5;
constructor(data, graph) {
// Инициализация оболочки
const article = document.createElement("article");
article.id = graph.nodes.size;
article.classList.add("node", "unselectable");
article.style.top =
graph.shell.offsetHeight / 2 -
this.#diameter / 2 +
(0.5 - Math.random()) * 500 +
"px";
article.style.left =
graph.shell.offsetWidth / 2 -
this.#diameter / 2 +
(0.5 - Math.random()) * 500 +
"px";
if (typeof data.title === "string") {
// Найден заголовок узла
// Инициализация заголовка
const title = document.createElement("h4");
title.innerText = data.title;
// Запись в оболочку
article.appendChild(title);
}
// Запись в свойство
this.element = article;
// Запись в документ
graph.shell.appendChild(article);
// Инициализация
this.init();
}
init() {
// Инициализация размера
this.element.style.width = this.element.style.height =
this.#diameter + this.#increase * this.inputs.size + "px";
}
};
#move = true;
#camera = true;
constructor(shell, camera = true) {
// Запись оболочки
if (shell instanceof HTMLElement) this.shell = shell;
// Инициализация ссылки на обрабатываемый объект
const _this = this;
// Перемещение камеры
if (camera === true) {
this.shell.onmousedown = function (e) {
// Начало переноса
if (_this.#camera) {
// Разрешено двигать камеру (оболочку)
// Инициализация координат
const coords = _this.shell.getBoundingClientRect();
const x = e.pageX - coords.left + pageXOffset;
const y = e.pageY - coords.top + pageYOffset;
// Инициализация функции переноса
function move(e) {
// Запись нового отступа от верха
_this.shell.style.top = e.pageY - y + "px";
// Запись нового отступа от лева
_this.shell.style.left = e.pageX - x + "px";
}
// Перенос
document.onmousemove = move;
}
// Конец переноса
_this.shell.onmouseup = function () {
document.onmousemove = null;
_this.shell.onmouseup = null;
};
};
// Перещапись событий браузера (чтобы не дёргалось)
_this.shell.ondragstart = null;
}
}
write = function (data = {}) {
if (typeof data === "object") {
// Получен обязательный входной параметр в правильном типе
// Инициализация узла
const node = new this.node(data, this);
// Инициализация ссылки на обрабатываемый объект
const _this = this;
// Запрет движения камеры при наведении на узел (чтобы двигать узел)
node.element.onmouseover = function (e) {
_this.#camera = false;
};
// Снятие запрета движения камеры
node.element.onmouseout = function (e) {
_this.#camera = true;
};
if (this.#move) {
// Разрешено перемещать узлы
node.element.onmousedown = function (onmousedown) {
// Начало переноса
// Позиционирование над остальными узлами
node.element.style.zIndex = 550;
if (!_this.#camera) {
// Запрещено двигать камеру (оболочку)
// Инициализация координат
const n = node.element.getBoundingClientRect();
const s = _this.shell.getBoundingClientRect();
// Инициализация функции переноса
function move(onmousemove) {
// Запись нового отступа от верха
node.element.style.top =
onmousemove.pageY -
(onmousedown.pageY - n.top + s.top + pageYOffset) +
"px";
// Запись нового отступа от лева
node.element.style.left =
onmousemove.pageX -
(onmousedown.pageX - n.left + s.left + pageXOffset) +
"px";
for (const [connection, target] of node.outputs) {
// Перебор исходящих соединений
// Инициализация координат для линии
const x1 = parseInt(node.element.style.left);
const y1 = parseInt(node.element.style.top);
// Запись новой координаты по горизонтали
connection.children[0].setAttribute(
"x1",
(isNaN(x1) ? 0 : x1) + node.element.offsetWidth / 2
);
// Запись новой координаты по вертикали
connection.children[0].setAttribute(
"y1",
(isNaN(y1) ? 0 : y1) + node.element.offsetHeight / 2
);
}
for (const [connection, target] of node.inputs) {
// Перебор входящих соединений
// Инициализация координат для линии
const x2 = parseInt(node.element.style.left);
const y2 = parseInt(node.element.style.top);
// Запись новой координаты по горизонтали
connection.children[0].setAttribute(
"x2",
(isNaN(x2) ? 0 : x2) + node.element.offsetWidth / 2
);
// Запись новой координаты по вертикали
connection.children[0].setAttribute(
"y2",
(isNaN(y2) ? 0 : y2) + node.element.offsetHeight / 2
);
}
}
// Перенос
document.onmousemove = move;
}
// Конец переноса
node.element.onmouseup = function () {
// Очистка обработчиков событий
document.onmousemove = null;
node.element.onmouseup = null;
// Позиционирование вместе остальными узлами
node.element.style.zIndex = 500;
};
};
// Перещапись событий браузера (чтобы не дёргалось)
node.element.ondragstart = null;
}
// Запись в реестр
this.nodes.add(node);
return node;
}
};
connect = function (from, to) {
if (from instanceof this.node && to instanceof this.node) {
// Получены обязательные входные параметры в правильном типе
// Инициализация оболочки
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.id = this.connections.size;
svg.classList.add("connection");
svg.setAttribute("data-from", from.element.id);
svg.setAttribute("data-to", to.element.id);
// Инициализация координат для линии
let x1 = parseInt(from.element.style.left);
x1 = isNaN(x1) ? 0 : x1;
let y1 = parseInt(from.element.style.top);
y1 = isNaN(y1) ? 0 : y1;
let x2 = parseInt(to.element.style.left);
x2 = isNaN(x2) ? 0 : x2;
let y2 = parseInt(to.element.style.top);
y2 = isNaN(y2) ? 0 : y2;
// Инициализация оболочки
const line = document.createElementNS(
"http://www.w3.org/2000/svg",
"line"
);
line.setAttribute("x1", x1 + from.element.offsetWidth / 2);
line.setAttribute("y1", y1 + from.element.offsetHeight / 2);
line.setAttribute("x2", x2 + to.element.offsetWidth / 2);
line.setAttribute("y2", y2 + to.element.offsetHeight / 2);
line.setAttribute("stroke", "grey");
line.setAttribute("stroke-width", "8px");
// Запись в оболочку
svg.append(line);
// Запись в документ
this.shell.appendChild(svg);
// Запись соединений в реестры узлов
from.outputs.set(svg, to);
to.inputs.set(svg, from);
// Запись в реестр ядра
this.connections.add([from, to]);
// Реинициализация узла-получателя
to.init();
return svg;
}
};
}