added options for nodes generation and block some events
This commit is contained in:
parent
7bb1772164
commit
c3a8dad9de
233
graph.js
233
graph.js
|
@ -1,6 +1,6 @@
|
||||||
import Victor from "https://cdn.skypack.dev/victor@1.1.0";
|
import Victor from "https://cdn.skypack.dev/victor@1.1.0";
|
||||||
|
|
||||||
("use strict");
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
@ -97,40 +97,152 @@ class graph {
|
||||||
// Инициализация HTML-элемента узла
|
// Инициализация HTML-элемента узла
|
||||||
const article = document.createElement("article");
|
const article = document.createElement("article");
|
||||||
article.id = operator.nodes.size;
|
article.id = operator.nodes.size;
|
||||||
article.classList.add("node", "unselectable");
|
article.classList.add(data.color ?? 'white', "node", "unselectable");
|
||||||
if (typeof data.href === "string") {
|
if (typeof data.href === "string") {
|
||||||
article.href = data.href;
|
article.href = data.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Инициализация заголовка-ссылки
|
// Инициализация заголовка
|
||||||
const title = document.createElement("a");
|
const title = document.createElement("h4");
|
||||||
title.innerText = data.title ?? data.link ?? "Неизвестно";
|
title.classList.add('title');
|
||||||
if (typeof data.link === "string") title.href = data.link;
|
title.innerText = data.title ?? null;
|
||||||
|
|
||||||
// Запись в оболочку
|
// Запись в оболочку
|
||||||
article.appendChild(title);
|
article.appendChild(title);
|
||||||
|
|
||||||
if (typeof data.description === "string") {
|
// Инициализация описания
|
||||||
// Найдено описание узла
|
const description = document.createElement("div");
|
||||||
|
description.classList.add('description');
|
||||||
|
description.title = data.popup ?? null;
|
||||||
|
|
||||||
// Инициализация заголовка
|
/**
|
||||||
const description = document.createElement("p");
|
* Показать описание
|
||||||
description.innerText = data.description;
|
*/
|
||||||
description.style.display = "none";
|
function show() {
|
||||||
article.setAttribute(
|
// Отображение описания
|
||||||
"onclick",
|
description.style.display = null;
|
||||||
"const description = this.getElementsByTagName('p')[0]; description.style.display === 'none' ? description.style.display = null : description.style.display = 'none'"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Запись в оболочку
|
// Расположение выше остальных узлов
|
||||||
article.appendChild(description);
|
article.style.zIndex = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Скрыть описание
|
||||||
|
*/
|
||||||
|
function hide() {
|
||||||
|
// Скрытие описания
|
||||||
|
description.style.display = 'none';
|
||||||
|
|
||||||
|
// Удаление расположения выше других узлов
|
||||||
|
article.style.zIndex = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запись блокировки открытия описания в случае, если был перемещён узел
|
||||||
|
title.onmousedown = (onmousedown) => {
|
||||||
|
// Запись открытия описания
|
||||||
|
title.onclick = (onclick) => { show() }
|
||||||
|
|
||||||
|
title.onmousemove = (onmousemove) => {
|
||||||
|
// Курсор сдвинут более чем на 15 пикселей?
|
||||||
|
if (Math.abs(onmousedown.pageX - onmousemove.pageX) > 15 || Math.abs(onmousedown.pageY - onmousemove.pageY) > 15) {
|
||||||
|
// Запись иконки курсора
|
||||||
|
title.style.cursor = 'grabbing';
|
||||||
|
|
||||||
|
// Запись события для возврата иконки курсора
|
||||||
|
title.onmouseup = fn => {
|
||||||
|
// Удаление событий
|
||||||
|
title.onmouseup = title.onmousemove = null;
|
||||||
|
|
||||||
|
// Удаление иконки курсора
|
||||||
|
title.style.cursor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
title.onclick = (onclick) => { return false }
|
||||||
|
} else title.onclick = (onclick) => { show() }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Запись в оболочку
|
||||||
|
article.appendChild(description);
|
||||||
|
|
||||||
|
// Инициализация левой фигуры для обёртки текста
|
||||||
|
const left = document.createElement("span");
|
||||||
|
left.classList.add('left', 'wrapper');
|
||||||
|
|
||||||
|
// Запись в описание
|
||||||
|
description.appendChild(left);
|
||||||
|
|
||||||
|
// Инициализация правой фигуры для обёртки текста
|
||||||
|
const right = document.createElement("span");
|
||||||
|
right.classList.add('right', 'wrapper');
|
||||||
|
|
||||||
|
// Запись в описание
|
||||||
|
description.appendChild(right);
|
||||||
|
|
||||||
|
// Инициализация ссылки на источник
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.innerText = typeof data.link === 'object' && typeof data.link.name === 'string' ? data.link.name : null;
|
||||||
|
a.href = typeof data.link === 'object' && typeof data.link.href === 'string' ? data.link.href : null;
|
||||||
|
a.classList.add(...(typeof data.link === 'object' && typeof data.link.class === 'object' ? data.link.class : []));
|
||||||
|
a.title = typeof data.link === 'object' && typeof data.link.title === 'string' ? data.link.title : null;
|
||||||
|
|
||||||
|
// Блокировка событий браузера (чтобы не мешать переноса узла)
|
||||||
|
a.ondragstart = a.onselectstart = fn => { return false };
|
||||||
|
|
||||||
|
// Запись блокировки перехода по ссылке в случае, если был перемещён узел
|
||||||
|
a.onmousedown = (onmousedown) => {
|
||||||
|
a.onmousemove = (onmousemove) => {
|
||||||
|
// Курсор сдвинут более чем на 15 пикселей?
|
||||||
|
if (Math.abs(onmousedown.pageX - onmousemove.pageX) > 15 || Math.abs(onmousedown.pageY - onmousemove.pageY) > 15) {
|
||||||
|
// Запись иконки курсора
|
||||||
|
a.style.cursor = 'grabbing';
|
||||||
|
|
||||||
|
// Запись события для возврата иконки курсора
|
||||||
|
a.onmouseup = fn => {
|
||||||
|
// Удаление событий
|
||||||
|
a.onmouseup = a.onmousemove = null;
|
||||||
|
|
||||||
|
// Удаление иконки курсора
|
||||||
|
a.style.cursor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.onclick = (onclick) => { return false }
|
||||||
|
} else a.onclick = (onclick) => { return true }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Запись в описание
|
||||||
|
description.appendChild(a);
|
||||||
|
|
||||||
|
// Запись текста в описание
|
||||||
|
const text = document.createElement("p");
|
||||||
|
text.innerText = data.description ?? null;
|
||||||
|
|
||||||
|
// Запись в оболочку
|
||||||
|
description.appendChild(text);
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof data.cover === "string"
|
||||||
|
) {
|
||||||
|
// Получены другие HTML-элементы
|
||||||
|
|
||||||
|
// Инициализация левой фигуры для обёртки текста
|
||||||
|
const cover = document.createElement("img");
|
||||||
|
cover.src = data.cover;
|
||||||
|
cover.alt = data.title ?? null;
|
||||||
|
cover.classList.add('cover', 'unselectable');
|
||||||
|
|
||||||
|
// Запись в описание
|
||||||
|
description.appendChild(cover);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof data.append === "HTMLCollection" ||
|
typeof data.append === "HTMLCollection" ||
|
||||||
typeof data.append === "HTMLElement"
|
typeof data.append === "HTMLElement"
|
||||||
) {
|
) {
|
||||||
// Найдены другие HTML-элементы узла
|
// Получены другие HTML-элементы
|
||||||
|
|
||||||
// Запись в оболочку
|
// Запись в оболочку
|
||||||
article.appendChild(data.append);
|
article.appendChild(data.append);
|
||||||
|
@ -139,6 +251,15 @@ class graph {
|
||||||
// Запись в документ
|
// Запись в документ
|
||||||
operator.shell.appendChild(article);
|
operator.shell.appendChild(article);
|
||||||
|
|
||||||
|
// Запись диаметра описания в зависимости от размера заголовка (чтобы вмещался)
|
||||||
|
description.style.width = description.style.height = (a.offsetWidth === 0 ? 50 : a.offsetWidth) * 3 + 'px';
|
||||||
|
|
||||||
|
// Запись отступа заголовка (чтобы был по центру описания)
|
||||||
|
a.style.left = description.offsetWidth / 2 - a.offsetWidth / 2 + 'px';
|
||||||
|
|
||||||
|
// Сокрытие описания (выполняется после расчёта размера потому, что иначе размер будет недоступен)
|
||||||
|
description.style.display = "none";
|
||||||
|
|
||||||
// Запись в свойство
|
// Запись в свойство
|
||||||
this.#element = article;
|
this.#element = article;
|
||||||
|
|
||||||
|
@ -151,11 +272,11 @@ class graph {
|
||||||
// Перемещение
|
// Перемещение
|
||||||
this.move(
|
this.move(
|
||||||
operator.shell.offsetWidth / 2 -
|
operator.shell.offsetWidth / 2 -
|
||||||
this.#diameter / 2 +
|
this.#diameter / 2 +
|
||||||
(0.5 - Math.random()) * 500,
|
(0.5 - Math.random()) * 500,
|
||||||
operator.shell.offsetHeight / 2 -
|
operator.shell.offsetHeight / 2 -
|
||||||
this.#diameter / 2 +
|
this.#diameter / 2 +
|
||||||
(0.5 - Math.random()) * 500,
|
(0.5 - Math.random()) * 500,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true
|
true
|
||||||
|
@ -174,6 +295,14 @@ class graph {
|
||||||
this.element.style.width = this.element.style.height =
|
this.element.style.width = this.element.style.height =
|
||||||
this.#diameter + "px";
|
this.#diameter + "px";
|
||||||
|
|
||||||
|
// Инициализация описания
|
||||||
|
const description = this.element.getElementsByClassName('description')[0];
|
||||||
|
|
||||||
|
// Запись отступа описания (чтобы был по центру узла)
|
||||||
|
description.style.display = null;
|
||||||
|
description.style.marginLeft = description.style.marginTop = (this.element.offsetWidth - description.offsetWidth) / 2 + 'px';
|
||||||
|
description.style.display = 'none';
|
||||||
|
|
||||||
// Инициализация ссылки на ядро
|
// Инициализация ссылки на ядро
|
||||||
const _this = this;
|
const _this = this;
|
||||||
|
|
||||||
|
@ -501,10 +630,10 @@ class graph {
|
||||||
if (
|
if (
|
||||||
!node.actions.pushing ||
|
!node.actions.pushing ||
|
||||||
between.length() >
|
between.length() >
|
||||||
(node.diameter + _this.diameter) / 2 +
|
(node.diameter + _this.diameter) / 2 +
|
||||||
distance +
|
distance +
|
||||||
increase +
|
increase +
|
||||||
(typeof add === "number" ? add : 0) ||
|
(typeof add === "number" ? add : 0) ||
|
||||||
--iterations <= 0
|
--iterations <= 0
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
@ -539,10 +668,10 @@ class graph {
|
||||||
if (
|
if (
|
||||||
node.actions.pushing &&
|
node.actions.pushing &&
|
||||||
between.length() <=
|
between.length() <=
|
||||||
(node.diameter + _this.diameter) / 2 +
|
(node.diameter + _this.diameter) / 2 +
|
||||||
distance +
|
distance +
|
||||||
increase +
|
increase +
|
||||||
(typeof add === "number" ? add : 0)
|
(typeof add === "number" ? add : 0)
|
||||||
)
|
)
|
||||||
setTimeout(move, between.length() / 100);
|
setTimeout(move, between.length() / 100);
|
||||||
}
|
}
|
||||||
|
@ -632,10 +761,10 @@ class graph {
|
||||||
if (
|
if (
|
||||||
!node.actions.pulling ||
|
!node.actions.pulling ||
|
||||||
between.length() <=
|
between.length() <=
|
||||||
(node.diameter + _this.diameter) / 2 +
|
(node.diameter + _this.diameter) / 2 +
|
||||||
distance +
|
distance +
|
||||||
increase +
|
increase +
|
||||||
(typeof add === "number" ? add : 0) ||
|
(typeof add === "number" ? add : 0) ||
|
||||||
--iterations <= 0
|
--iterations <= 0
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
@ -669,10 +798,10 @@ class graph {
|
||||||
if (
|
if (
|
||||||
node.actions.pulling &&
|
node.actions.pulling &&
|
||||||
between.length() >
|
between.length() >
|
||||||
(node.diameter + _this.diameter) / 2 +
|
(node.diameter + _this.diameter) / 2 +
|
||||||
distance +
|
distance +
|
||||||
increase +
|
increase +
|
||||||
(typeof add === "number" ? add : 0)
|
(typeof add === "number" ? add : 0)
|
||||||
)
|
)
|
||||||
return setTimeout(
|
return setTimeout(
|
||||||
move,
|
move,
|
||||||
|
@ -715,8 +844,8 @@ class graph {
|
||||||
? value === "true"
|
? value === "true"
|
||||||
? true
|
? true
|
||||||
: value === "false"
|
: value === "false"
|
||||||
? false
|
? false
|
||||||
: value
|
: value
|
||||||
: buffer;
|
: buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -790,22 +919,22 @@ class graph {
|
||||||
line.setAttribute(
|
line.setAttribute(
|
||||||
"x1",
|
"x1",
|
||||||
(isNaN((buffer = parseInt(from.element.style.left))) ? 0 : buffer) +
|
(isNaN((buffer = parseInt(from.element.style.left))) ? 0 : buffer) +
|
||||||
from.element.offsetWidth / 2
|
from.element.offsetWidth / 2
|
||||||
);
|
);
|
||||||
line.setAttribute(
|
line.setAttribute(
|
||||||
"y1",
|
"y1",
|
||||||
(isNaN((buffer = parseInt(from.element.style.top))) ? 0 : buffer) +
|
(isNaN((buffer = parseInt(from.element.style.top))) ? 0 : buffer) +
|
||||||
from.element.offsetHeight / 2
|
from.element.offsetHeight / 2
|
||||||
);
|
);
|
||||||
line.setAttribute(
|
line.setAttribute(
|
||||||
"x2",
|
"x2",
|
||||||
(isNaN((buffer = parseInt(to.element.style.left))) ? 0 : buffer) +
|
(isNaN((buffer = parseInt(to.element.style.left))) ? 0 : buffer) +
|
||||||
to.element.offsetWidth / 2
|
to.element.offsetWidth / 2
|
||||||
);
|
);
|
||||||
line.setAttribute(
|
line.setAttribute(
|
||||||
"y2",
|
"y2",
|
||||||
(isNaN((buffer = parseInt(to.element.style.top))) ? 0 : buffer) +
|
(isNaN((buffer = parseInt(to.element.style.top))) ? 0 : buffer) +
|
||||||
to.element.offsetHeight / 2
|
to.element.offsetHeight / 2
|
||||||
);
|
);
|
||||||
line.setAttribute("stroke", "grey");
|
line.setAttribute("stroke", "grey");
|
||||||
line.setAttribute("stroke-width", "8px");
|
line.setAttribute("stroke-width", "8px");
|
||||||
|
@ -851,14 +980,14 @@ class graph {
|
||||||
this.element.children[0].setAttribute(
|
this.element.children[0].setAttribute(
|
||||||
x,
|
x,
|
||||||
(isNaN((buffer = parseInt(node.element.style.left))) ? 0 : buffer) +
|
(isNaN((buffer = parseInt(node.element.style.left))) ? 0 : buffer) +
|
||||||
node.element.offsetWidth / 2
|
node.element.offsetWidth / 2
|
||||||
);
|
);
|
||||||
|
|
||||||
// Запись отступа (координаты по вертикали)
|
// Запись отступа (координаты по вертикали)
|
||||||
this.element.children[0].setAttribute(
|
this.element.children[0].setAttribute(
|
||||||
y,
|
y,
|
||||||
(isNaN((buffer = parseInt(node.element.style.top))) ? 0 : buffer) +
|
(isNaN((buffer = parseInt(node.element.style.top))) ? 0 : buffer) +
|
||||||
node.element.offsetHeight / 2
|
node.element.offsetHeight / 2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -911,7 +1040,7 @@ class graph {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Перещапись событий браузера (чтобы не дёргалось)
|
// Блокировка событий браузера (чтобы не дёргалось)
|
||||||
_this.shell.ondragstart = null;
|
_this.shell.ondragstart = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -943,7 +1072,7 @@ class graph {
|
||||||
// Начало переноса
|
// Начало переноса
|
||||||
|
|
||||||
// Позиционирование над остальными узлами
|
// Позиционирование над остальными узлами
|
||||||
node.element.style.zIndex = 550;
|
node.element.style.zIndex = 5000;
|
||||||
|
|
||||||
if (!_this.#camera) {
|
if (!_this.#camera) {
|
||||||
// Запрещено двигать камеру (оболочку)
|
// Запрещено двигать камеру (оболочку)
|
||||||
|
@ -960,9 +1089,9 @@ class graph {
|
||||||
// Перемещение
|
// Перемещение
|
||||||
node.move(
|
node.move(
|
||||||
onmousemove.pageX -
|
onmousemove.pageX -
|
||||||
(onmousedown.pageX - n.left + s.left + pageXOffset),
|
(onmousedown.pageX - n.left + s.left + pageXOffset),
|
||||||
onmousemove.pageY -
|
onmousemove.pageY -
|
||||||
(onmousedown.pageY - n.top + s.top + pageYOffset),
|
(onmousedown.pageY - n.top + s.top + pageYOffset),
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
true
|
true
|
||||||
|
@ -983,7 +1112,7 @@ class graph {
|
||||||
node.actions.collision = node.actions.pushing = node.actions.pulling = true;
|
node.actions.collision = node.actions.pushing = node.actions.pulling = true;
|
||||||
|
|
||||||
// Позиционирование вместе остальными узлами
|
// Позиционирование вместе остальными узлами
|
||||||
node.element.style.zIndex = 500;
|
node.element.style.zIndex = null;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue