Итак, вы посетили D3js.org и были поражены тем, что строки кода можно преобразовать в формы в DOM. Что еще более важно, эти формы собраны вместе, чтобы создать приятное зрелище! Мы все были там.
Однако вы посмотрите на предоставленный образец кода и сразу заметите крутой путь обучения.
Но не волнуйтесь, во всем есть некая закономерность. Когда вы это заметите, дорога вперед не будет казаться такой устрашающей, как несколько минут назад. Вот:
- Очистите данные
- Сориентируйте данные с помощью шкал (при необходимости)
- Подготовьте DOM с помощью селекторов
- Начните визуализацию с форм
- Добавьте текст, чтобы быть красивым
В этой статье я рассмотрю пример пузырьковой диаграммы Майка Бостока, потому что он, вероятно, привлек ваше внимание, как и мое. Отказ от ответственности: пузырьковая диаграмма, хотя и приятна на вид, не является лучшим типом диаграммы для демонстрации данных, поскольку она «менее точна с точки зрения восприятия, чем гистограммы».
1) Данные
Все проекты D3 начинаются с данных. Конечная цель состоит в том, чтобы в конце концов выделить понимание из произвольного набора данных.
Данные могут быть любыми, если вы очищаете их до объектов, которые можно загрузить в массив. Затем этот массив объектов будет привязан к его собственному элементу, а затем добавлен в DOM. Но об этом позже.
Данные могут поступать во многих формах. Существует ряд популярных форматов данных, таких как JSON или файлы значений, разделенных запятыми (CSV), а также XMLHttpRequests, текстовые файлы, фрагмент документа HTML, фрагмент документа XML и файлы значений, разделенных табуляцией (TSV). Данные в нашем примере - это файл CSV, который содержит значения для иерархии классов Flare.
#flare.csv id,value flare, flare.analytics, flare.analytics.cluster, flare.analytics.cluster.AgglomerativeCluster,3938 flare.analytics.cluster.CommunityStructure,3812 flare.analytics.graph, flare.analytics.graph.BetweennessCentrality,3534 flare.analytics.graph.LinkDistance,5731 Truncated for brevity….
D3 предоставляет методы для анализа и подготовки данных, чтобы сделать их более управляемыми. В нашем примере используется `d3.csv (« файл / путь / », функция обратного вызова ])`. Этот метод создает объект для каждой строки данных и использует его заголовки в качестве имен свойств.
d3.csv(“flare.csv”, function(d) { d.value = +d.value; if (d.value) return d; } /* console.log(d[0]) => { id: flare.analytics.cluster.AgglomerativeCluster, value: 3938 }*/
Заметьте, мощной особенностью D3 является его способность использовать функции для получения значений. Функция дает нам гибкость в форме наших данных. В приведенном выше фрагменте кода оператор if используется для возврата массива объектов только для строк со значением.
Кроме того, наши данные по своей сути являются иерархическими, поэтому были предприняты дальнейшие шаги для выделения структуры с помощью метода d3.hierarchy (data [, children]).
d3.csv(“flare.csv”, function(d) { d.value = +d.value; if (d.value) return d; }, function(error, classes){ //classes === array of data if (error) throw error; var root = d3.hierarchy({children: classes})//data is fed in .sum(function(d) { return d.value; }) .each(function(d) { if (id = d.data.id) { var id, i = id.lastIndexOf(“.”); d.id = id; d.package = id.slice(0, i); //Property used by color scale d.class = id.slice(i + 1); //Property used when by text nodes } });
После прохождения этой функции каждый элемент данных будет иметь дополнительные свойства, которые будут использоваться в других функциях и методах D3, составляющих визуал. Все данные были изменены следующим образом:
//Before { id: "flare.analytics.cluster.AgglomerativeCluster" value: 3938 } //After { class: "AgglomerativeCluster" data: Object { id: "flare.analytics.cluster.AgglomerativeCluster", value: 3938 } depth: 1 height: 0 id: "flare.analytics.cluster.AgglomerativeCluster" package: "flare.analytics.cluster" parent: Object { height: 1, depth: 0, value: 956129, … } }
2) Весы
Так же, как данные бывают разных форм, данные также могут иметь разные размеры. В результате, как правило, необходимо адаптировать размеры набора данных, чтобы они соответствовали рамкам визуального элемента. D3 предлагает несколько мощных функций масштабирования, которые полностью решают проблемы масштабирования. Вот доступные шкалы и варианты их использования:
- Линейный: преобразует количественное значение в данном интервале (области) в значение в другом заданном интервале (диапазоне).
- Порядковый: для неколичественных шкал, таких как имена, категории и т. Д.
- Идентичность: особый вид линейной шкалы, 1: 1, подходит для значений пикселей. вход == выход
- Степенная и логарифмическая шкалы: sqrt, pow, log - используются для экспоненциально возрастающих значений
- Шкалы квантования и квантиля: для дискретных наборов уникальных возможных значений для входов или выходов.
Продолжая наш пример, мы действительно должны взглянуть на минимальные и максимальные значения нашего набора данных. Которые:
minimum - { id: flare.query.methods._, value: 264 } maximum - { id: flare.vis.axis.Axis, value: 24593 }
Но для удобства наш пример не имеет строгих ограничений по размерам, которые нужно соблюдать, например, от 0% до 100% или какого-либо конкретного диапазона значений. Если бы это было так, мы бы, вероятно, использовали метод d3.scaleLinear (), который отлично подходит для адаптации числовых значений.
Скорее, нам просто нужно различать иерархию наших данных, назначая цвета с помощью свойства «пакет», которое было добавлено к каждому элементу данных в предыдущем разделе. Это достигается с помощью порядковой шкалы, которая отлично подходит для разумного кодирования данных, не являющихся числовыми по своей природе.
В нашем примере используется метод d3.scaleOrdinal () в сочетании с d3.schemeCategory20c для цветного кодирования каждого элемента визуала.
var color = d3.scaleOrdinal(d3.schemeCategory20c);
Эта цветовая функция позже вызывается, когда круги добавляются к модели DOM (рассматривается в следующем разделе).
3) Выбор
Теперь, когда наши данные подготовлены, мы можем наконец приступить к изучению механики D3, которая работает с помощью селекторов. Селекторы позволяют D3 устанавливать атрибуты или стили; регистрация слушателей событий; добавление, удаление или сортировка узлов; и изменение HTML или текстового содержимого.
Выбор работает путем нацеливания на определенные элементы и обновления их по мере изменения данных. Сложная часть выбора - это распознать то, что я лично назвал бы двумя разными типами событий.
1. Первоначальный отбор
Я определяю первоначальный выбор как одноразовое событие, когда тег HTML ‹svg› добавляется к корневому элементу модели DOM.
Прежде чем мы продолжим, тег ‹svg› представляет собой контейнер для двумерной векторной графики (также известной как масштабируемая векторная графика (SVG)). В частности, векторная графика не создается из пикселей. Скорее они созданы с путями, имеющими начальную и конечную точки. Это позволяет масштабировать их до большего или меньшего размера без потери качества изображения. Вот что делает визуальные эффекты такими резкими и ошеломляющими.
Возвращаясь к теме, вы обычно видите тег ‹svg›, добавленный непосредственно к ‹body›, например:
var svgContainer = d3.select(“body”).append(“svg”)
Однако в HTML-файле этого примера уже есть тег ‹svg› с атрибутами ширины и высоты, поэтому первоначальный выбор нацелен непосредственно на этот тег ‹svg›.
var svg = d3.select(“svg”), width = +svg.attr(“width”), height = +svg.attr(“height”);
2. Последующий отбор
Я определяю последующий выбор как событие, когда элемент данных или группа элементов данных (тег ‹g›) добавляется к существующему тегу ‹svg›. Вот где это может стать подавляющим. Во время последующего выбора к каждому элементу данных привязывается несколько методов для создания визуального элемента. Эти методы включают:
i) .selectAll («/ * имя класса элемента данных * /) - с помощью этого метода будут выбраны все элементы данных с указанным именем класса в DOM. Но как выбрать элементы, которых еще нет? Подробнее об этом чуть позже.
ii) .data (/ * массив данных * /) - Для каждого элемента данных в DOM (который еще не существует) предоставьте ему значение из нашего массива данных (корень),
iii) .attr () - используется для добавления атрибутов, таких как имена классов и размеры.
iv) Переходные случаи - в зависимости от состояния визуала D3 использует следующие методы, чтобы выяснить, как обрабатывать каждый элемент данных в теге ‹svg›.
a) Выбор .enter () содержит заполнители для любых отсутствующих элементов данных или элементов данных, которые еще не существуют. В этом случае обычно создаются новые элементы данных для входящих данных.
б) Выбор .update () содержит существующие элементы, привязанные к данным. Это используется для обновления элемента данных.
c) Любые оставшиеся элементы попадают в выборку .exit () для удаления, поскольку они больше не нужны для визуального элемента.
В нашем примере ‹g› создается для каждого элемента данных нашего массива данных с помощью метода .enter (). Поскольку это статический визуальный элемент, в нашем примере не используются случаи .update () или .exit ().
var node = svg.selectAll(“.node”) .data(pack(root).leaves()) .enter().append(“g”) .attr(“class”, “node”) .attr(“transform”, function(d) { return “translate(“ + d.x + “,” + d.y + “)”; });
4) Формы
Большинство визуальных элементов данных состоит из нескольких форм (например, линий, прямоугольников, эллипсов, кругов). Чтобы построить фигуру, просто добавьте ее в контейнер ‹svg› или ‹g› и примените необходимые атрибуты. Чтобы сделать пузыри, просто добавьте круг и определите его так:
node.append(“circle”) .attr(“id”, function(d) { return d.id; }) .attr(“r”, function(d) { return d.r; }) .style(“fill”, function(d) { return color(d.package); });
D3 также обеспечивает гибкость для создания ваших собственных форм. Чтобы избежать чрезмерного усложнения основного паттерна, я не буду описывать его в этой статье.
5) Текст
Если вы последуете инструкциям и до этого момента использовали только скрытый код, вы бы получили пузырьковую диаграмму. Но вы не сможете отличить один пузырек от другого, не глядя в инспектор в наших инструментах разработчика, потому что там нет ярлыков. Даже если есть id.
Как вы, возможно, догадались, чтобы получить текст в каждом пузыре, к каждому тегу элемента данных ‹g› добавляется текстовый элемент для идентификации каждого пузыря. Это необходимо, поскольку формы не являются контейнерами. Текст в фигуре просто игнорируется. В этом примере добавляется такой текстовый элемент:
node.append(“text”) .attr(“clip-path”, function(d) { return “url(#clip-” + d.id + “)”; //Not covered in this article }) .selectAll(“tspan”) .data(function(d) { return d.class.split(/(?=[A-Z][^A-Z])/g); }) .enter().append(“tspan”) .attr(“x”, 0) .attr(“y”, function(d, i, nodes) { return 13 + (i — nodes.length / 2–0.5) * 10; }) .text(function(d) { return d; });
А как насчет остальных?
У пузырьковой диаграммы Майка есть и другие части, но я рассмотрел только основную закономерность, чтобы этот пост имел разумную длину.
Я уверен, вы заметили, что после раздела «первоначальный выбор» каждая функция визуального элемента была результатом простого добавления еще одного элемента HTML в DOM. Например, (круги, текст, клип-путь, заголовок).
Это все, что нужно для простых визуальных эффектов!
В заключение …
Библиотека D3 обширна, и, как следствие, кривая обучения невероятно крутая. Я надеюсь, что эта прогулка предоставила вам некоторый фундаментальный контекст, который поможет вам понять, на чем сосредоточиться, когда вы изучаете более сложные визуальные эффекты! Удачи!!
Дополнительные ресурсы: