Создание радиального дерева с использованием D3.js для JavaScript
В этой статье я опишу вам, как вы можете закодировать радиальное дерево на JavaScript, используя библиотеку D3.js (версия 6). Попутно я также рассмотрю ряд концепций, используемых в D3.js и SVG для HTML. Так что, даже если радиальные деревья вас не интересуют, вы все равно можете найти здесь полезную информацию.
Причина написания этого заключается в том, что я искал простое и понятное решение, но не нашел ни одного, которое работало бы.
Код, который вы найдете ниже, представляет собой синтез кода из статей [1], [2] и [3], которые вы можете найти в Справочниках в конце этой статьи. Код должен работать сразу.
По сути, цель состоит в том, чтобы создать радиальное дерево на основе некоторых данных, которые у нас есть, и которые уже имеют какую-то древовидную структуру.
Структурированные данные
Ниже приведен пример данных в формате JSON, который я буду использовать в этой статье. Вы можете видеть, что он имеет четкую древовидную структуру. Это взято из [2].
let root = {"name" : "A", "info" : "tst", "children" : [ {"name" : "A1", "children" : [ {"name" : "A12" }, {"name" : "A13" }, {"name" : "A14" }, {"name" : "A15" }, {"name" : "A16" } ] }, {"name" : "A2", "children" : [ {"name" : "A21" }, {"name" : "A22", "children" : [ {"name" : "A221" }, {"name" : "A222" }, {"name" : "A223" }, {"name" : "A224" } ]}, {"name" : "A23" }, {"name" : "A24" }, {"name" : "A25" }] }, {"name" : "A3", "children": [ {"name" : "A31", "children" :[ {"name" : "A311" }, {"name" : "A312" }, {"name" : "A313" }, {"name" : "A314" }, {"name" : "A315" } ]}] } ]};
Обзор Кодекса
Перед тем, как начать кодировать радиальное дерево, вам необходимо импортировать библиотеку D3.js. В этой статье мы будем использовать версию 6.
Импорт библиотеки D3.js
Разместите следующий код под элементом head своей HTML-страницы. Это позволит импортировать библиотеку D3.js для использования на вашей веб-странице.
<head> ... <script src="https://d3js.org/d3.v6.min.js"></script> ... </head>
Теперь мы можем приступить к работе с JavaScript.
Создайте элемент SVG, содержащий радиальное дерево
Сначала установите высоту и ширину. На мой взгляд, квадрат имеет наибольший смысл, учитывая, что радиальное дерево лучше всего подходит для квадратной формы.
let height = 600; let width = 600;
Затем мы позволяем D3 создать элемент SVG с указанными выше высотой и шириной для нас под элементом body.
let svg = d3.select('body') .append('svg') .attr('width', width) .attr('height', height);
Элемент SVG также может быть создан под другим элементом, например, div с определенным id, если необходимо.
Фактически, также возможно стилизовать элемент SVG с помощью CSS, включенного в элемент head.
Здесь мы придаем элементу SVG красивую рамку.
<head> ... <style> svg { border: solid 1px #ccc; } </style> ... </head>
Если вы запустите этот код, вы увидите пустое изображение SVG размером 600x600 пикселей с красивой рамкой вокруг него.
Создайте древовидную иерархию на основе данных
D3 требует, чтобы вы создали так называемый «корневой узел», прежде чем вы сможете создать иерархический макет - для дерева. Учитывая, что наши данные JSON уже находятся в иерархическом формате, мы можем просто использовать следующий код:
let input = root //our JSON data from the beginning of the article let data = d3.hierarchy(input)
Если наши данные не были в иерархическом формате, как, например, табличные данные, то вместо этого можно использовать функцию d3.stratify (). См. [6].
Создайте дерево D3, содержащее наши данные
Приступим к созданию нашего дерева.
Мы можем установить диаметр на 3/4 высоты. Это простое удобство. Вы можете использовать любое соотношение, подходящее для конкретного дерева. Может потребоваться некоторая настройка, чтобы ваше радиальное дерево правильно поместилось в поле SVG.
Учитывая, что мы создаем радиальное дерево, размер дерева определяется общими градусами, которые оно охватывает - в данном случае 2 * PI - и радиусом.
Если поставить здесь PI вместо 2 * PI, то получится половина радиального дерева.
Аксессор separa () необходим для установки расстояния между соседними узлами в дереве. При необходимости см. Ссылку здесь [5].
let diameter = height * 0.75; let radius = diameter / 2; let tree = d3.tree() .size([2 * Math.PI, radius]) .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });
Теперь, когда у нас есть пустое дерево, можно добавлять в него данные.
let treeData = tree(data);
Мы также можем получить узлы и ссылки из дерева.
let nodes = treeData.descendants(); let links = treeData.links();
Создание элемента SVG, содержащего радиальное дерево
Следующее, что мы можем сделать, это создать элемент g под элементом SVG. Этот элемент позволяет нам группировать различные элементы SVG вместе. Как [4] заявляет:
Элемент группы SVG, ‹g›, отлично подходит для логической группировки наборов связанных графических объектов. Эта групповая возможность позволяет легко добавлять стили, преобразования, интерактивность и даже анимацию для целых групп объектов.
Элемент g размещается в середине изображения SVG. Размещение элемента g можно выполнить только с помощью преобразования, поскольку он не имеет координат x и y.
Мы будем использовать имя переменной graphGroup, чтобы ссылаться на нее.
let graphGroup = svg.append('g') .attr('transform', "translate("+(width/2)+","+(height/2)+")");
Создание путей SVG для соединения узлов в дереве
Теперь мы можем создавать пути между всеми узлами в дереве. Они представлены переменной links.
Используя selectAll (), вы можете создать пустой выбор. С помощью data () каждая из ссылок объединяется с пустым выделенным фрагментом. В результате получается массив элементов, длина которого равна количеству ссылок. Однако все эти элементы пусты.
Для заполнения пустых элементов используется функция join (). Таким образом, пустые элементы преобразуются в элементы path (SVG).
Затем для атрибута class устанавливается значение link, а атрибут d заполняется с помощью функции d3.linkRadial (). . Последняя функция по существу создает радиальную структуру дерева.
graphGroup.selectAll(".link") .data(links) .join("path") .attr("class", "link") .attr("d", d3.linkRadial() .angle(d => d.x) .radius(d => d.y));
В случае, если мы хотим создать вертикальное или горизонтальное дерево вместо радиального, мы могли бы рассмотреть возможность использования d3.linkVertical () и d3.linkHorizontal () соответственно. См. [7].
Ссылки также можно стилизовать с помощью CSS, чтобы они были видны и выглядели правильно.
<head> ... <style> ... .link { fill: none; stroke: #ccc; stroke-width: 1.5px; } </style> ... </head>
Создание кругов и текста SVG для представления узлов дерева
Процесс работает так же, как и для ссылок, но немного сложнее.
Вместо использования join () с элементом SVG path мы используем элемент SVG g. Элемент g используется для группировки кругов и текста для узлов.
На этот раз атрибут class является узлом.
Кроме того, элемент g преобразуется таким образом, что он помещается в дерево.
let node = graphGroup .selectAll(".node") .data(nodes) .join("g") .attr("class", "node") .attr("transform", function(d){ return `rotate(${d.x * 180 / Math.PI - 90})` + `translate(${d.y}, 0)`; });
Теперь мы можем добавить кружок к элементу g. Этот круг будет представлять сам узел. Атрибут r используется для определения радиуса круга.
node.append("circle").attr("r", 1);
И, наконец, можно также добавить текст к каждому из узлов.
Здесь можно добавить различные текстовые атрибуты, такие как font-size и font-family.
Атрибуты dx и dy определяют положение текста по отношению к самому узлу.
text-anchor и преобразование используются вместе для правильного выравнивания текста по узлам, чтобы он оставался читаемым.
Наконец, к узлу добавляется фактический текст.
node.append("text") .attr("font-family", "sans-serif") .attr("font-size", 12) .attr("dx", function(d) { return d.x < Math.PI ? 8 : -8; }) .attr("dy", ".31em") .attr("text-anchor", function(d) { return d.x < Math.PI ? "start" : "end"; }) .attr("transform", function(d) { return d.x < Math.PI ? null : "rotate(180)"; }) .text(function(d) { return d.data.name; });
Полученное радиальное дерево
Когда вы запустите код, вы получите веб-страницу, содержащую радиальное дерево, подобное приведенному ниже:
Полный код
использованная литература
[1] « Радиальное аккуратное дерево Майка Бостока»
[2] « d3.js - От дерева к кластеру и радиальная проекция Свена JavaDude Хафнера»
[3] Тема« Радиальное дерево / кластер D3 v5 в StackOverflow»
[4] « Создание веб-приложений с SVG Дэвидом Дейли, Джоном Фростом и Доменико Страцзулло из Microsoft Press» (партнерская ссылка)