Автор оригинала: Zaid Humayun.
Я недавно использую D3 довольно недавно, чтобы построить панель инструментов аналитики. Основная причина, по которой я пошел с D3, заключался в том, что я устал иметь дело с ограничениями различных библиотек по картам и пытается понять их документацию.
Я оказался в ситуации, когда я проводил больше времени, пытаясь выяснить, была ли библиотека, которую я использую, мог сделать то, что я хотел, а не на самом деле пытаться выяснить, как делать то, что я хотел.
С D3 почти никогда не бывает, если вы можете что-то сделать. Это всегда о том, как вы можете что-то сделать.
Предоставлено, попасть в GRIPS с API D3 – это вызов Mammoth, но это стоит хлопот.
В этой статье я собираюсь попытаться дать вам визуальное представление моего понимания D3 после работы с ним на некоторое время.
ИБП и падения D3
То, что я люблю о D3, это то, что она построена на вершине наименьших примитивов, и у вас есть доступ ко всем этим примитикам.
Если вы должны были использовать типичную библиотеку Charting для реагирования, вы, вероятно, сделаете что-то вроде этого
import { BarChart } from 'generic-charting-library'
export default class Chart() {
render() {
return (
Вышеуказанное отлично подходит для большинства случаев использования. Тем не менее, в тот момент, когда вы хотите сделать что-либо немного более сложное, как взаимодействующие с гистограммой с гирл-диаграммой (думают, что просто отображение наконечника инструмента), мой личный опыт заключается в том, что он превращается в войну с документацией библиотеки.
Во-первых, вы должны выяснить, это даже возможно сделать это с библиотекой, а затем вы должны понять, как это сделать.
Первая часть, в моем опыте, это жесткий бит. Это довольно неприятно, чтобы искать то, что вы не уверены. Однако с D3, это не так. Довольно все, что вы хотите сделать, можно сделать с D3. Это просто вопрос выяснения, как это сделать.
Недостатком, конечно, является то, что API и документация D3 настолько обширны и обширно, что вы в конечном итоге имеете разроздывающие части знаний о том, как все работает. Это также является следствием того, как я решил изучить D3, то есть что-то строить с ним. Когда вы решите построить что-то с технологией, вы только смотрите на куски, относящиеся к тому, что вы в настоящее время строят.
Скажите, например, вы хотели построить гистограмму. Ну, вы, вероятно, собираетесь посмотреть на что-то подобное, как определить и размещать оси на веб-странице. Затем вы, вероятно, посмотрите, как определить фактические бары самих баров. Это четко определенные проблемы и имеют простые решения.
Я взял почти такой же маршрут и оказался в расстраивающем месте, где я мог получить вещи на работу, но я не мог понять, как все это было вместе.
Я собираюсь объяснить свой продукт, как я собрал все разные части.
Большая картина
Следующее изображение – это упрощенная гистограмма (то, что мы постараемся воссоздать на этом посте)
Теперь вот такая же гистограмма с различными компонентами, отмеченными. По компонентам я имею в виду разные вещи, которые нам нужно будет беспокоиться при создании гистограммы с использованием D3.
- Ось X
- Ось Y
- Название
- Бары (я считаю всех них как один)
- Расстояние между клещами на оси X
- Фактическая область диаграммы
- Расстояние между клещами на оси Y
Мы исследуем все, кроме расстояния между галочками подробно. Расстояние между клещами позаботится о себе при выявлении того, как построить оси.
Маленькие вещи, которые составляют большую картину
Перед тем, как мы сможем даже начать составлять небольшие кусочки, которые составляют большую картину, нам нужно понять один элемент, который является частью каждого компонента на графике выше: элемент SVG.
Элемент SVG
Диаграмма D3 в основном состоит из элементов SVG. На диаграмме бар выше, ось X, ось Y, каждый отдельный бар – все экземпляры элементов SVG.
Я рекомендую читать Эта страница Чтобы лучше понять, что является SVG. SVG по существу, как вы описываете 2D графику на веб-странице.
Наиболее основным примером элемента SVG является элемент круга.
Смотрите ручку QGMXZO Zaid Humayun ( @redixhumayun ) на Кодепен Отказ
Посмотрите на кодепене выше, и вы должны увидеть определение для круга SVG внутри HTML-файла.
Я советую пройти через документацию SVG на MDN (связанный выше) и ознакомиться с ним. D3 делает широкое использование SVG.
G в svg
Существует определенный вид элемента SVG, называемый G элементом. Похоже на то, как мы определили элемент круга выше, мы определяем это с
Думать о G элемент как похож на Div Элемент, который используется в качестве контейнера в HTML. Они оба используются для группировки определенных элементов.
Прочитайте документацию MDN для G здесь
Если вы хотите понять, почему люди используют G Элемент, читать это
Данные, которые нам нужны
Прежде чем мы продолжим дальше, давайте быстро создадим данные, которые мы будем использовать. Вот образец файла JSON, мы можем использовать для создания гистограммы.
[
{
"key": "A",
"value": 20
},
{
"key": "B",
"value": 40
},
{
"key": "C",
"value": 80
},
{
"key": "D",
"value": 55
},
{
"key": "E",
"value": 70
}
]Область диаграммы
Начнем с создания простой области диаграммы сначала. Способ создания области диаграммы – настроить базовый элемент SVG, а затем назначить атрибут ViewBox.
Просто игнорируйте, какой атрибут ViewBox находится сейчас. Это не имеет отношения к обсуждению этого поста.
Вы еще ничего не увидите на экране, потому что таблица прозрачна в этой точке. Однако, если вы используете инспектор браузера, вы увидите элемент SVG.
Мы также определим некоторые размеры для нашей области диаграммы, как высота, ширина и поля.
const height = 600
const width = 800
const margin = { top: 15, right: 30, bottom: 15, left: 30 }Теперь, когда мы определили размеры, которые нам нужно на самом деле создать область для нашей таблицы в DOM. Для этого нам нужно использовать что-то называемое D3.Выберите
Думать об этом точно так же, как набор Документ. Gtectelementby [x] Команды Дом предлагает родом.
Когда вы используете что-то вроде D3.Выберите ('. Chart') Вы спрашиваете D3, чтобы выбрать элемент с классом с именем диаграммы.
Обратите внимание, что мы сохраняем выбор внутри переменной. Это будет важно позже.
Когда вы выбираете что-то с D3.Выберите , D3 позволяет использовать метод цепочки для изменения атрибутов, таких как ширина и высота, как я здесь сделал.
const chart = d3.select(".chart")
.attr("width", width)
.attr("height", height)Что мы получим что-то вроде следующего изображения
Не беспокойтесь о полях сейчас. Мы позаботимся об этом позже.
Определение осей
Теперь мы начинаем с мясистой части D3: создание и размещение наших осей.
Прежде чем мы сможем начать, нам нужно понять что-то фундаментальное о том, как рабочие оси D3 работают: они, по сути, являются отображением из одного набора значений в другой набор значений.
Два набора значений называются доменом и диапазоном. Картирование D3 работает от домена на диапазон.
Я определил два действительно простых номера строк для иллюстрации домена и диапазона. Диапазон точно такой же, как домен с двойным количеством маркировки.
В этом примере его действительно легко увидеть, как домен может быть сопоставлен на диапазон. Вам просто нужно умножить значение на 2, поскольку диапазон удвоил количество клещей и имеет то же самое начальное значение тикания 0.
Я нарисовал две пунктирные линии, чтобы показать следующие сопоставления
2 -> 4 5.5 -> 11
Теперь D3 не ограничивается только для реальных чисел (или даже просто чисел), чтобы определить масштабы. Вы можете даже использовать символы для определения ваших весов.
Шкала Y.
Мы начнем с Й масштаба.
D3 имеет различные виды чешуек, но тот, который мы будем использовать, называется линейной шкалой.
Чтобы определить масштаб, нам нужно две вещи: домен и диапазон.
Мы будем использовать простое, глупое правило для определения нашего домена. Предположим, что минимальное значение, которое мы можем иметь для одной из наших категорий, равно 0, а максимальное значение равно 100. Нет отрицательных чисел. Затем домен становится [0, 100]
const y = d3.scaleLinear()
.domain([0, 100])
.range([height - margin.bottom, margin.top])Одна вещь, которую нам нужно для изучения, вот диапазон. Мне немного потребовалось, чтобы понять, почему диапазон, кажется, находится в «обратном». Моя начальная мысль заключалась в том, что диапазон должен быть [Margin.top, рост - маржа. Соторник] Отказ Но мы хотим нашу ось Y для диаграммы начать внизу и движется вертикально вверх.
Мы рассмотрим следующие два сценария на последующей диаграмме, чтобы изучить это.
1. .range([height - margin.bottom, margin.top]) 2. .range([margin.top, height - margin.bottom])
Важное различие между двумя сценариями заключается в том, что в первом сценарии мы обрабатываем значение высоты как нашего «нулевого» значения. Во втором сценарии мы обращаемся к Margin.top ценность как нашего «нулевое» значение.
Интерпретируемый другим способом, нижняя часть оси Y является нашим «нулевым» значением в первом сценарии, а верхняя часть оси Y является нашим «нулевым» значением во втором сценарии.
На изображении выше сценарий 1 находится слева и сценарий 2 находится справа. Вы можете увидеть направление движения для домена в каждом изображении.
В сценарии 1 домен растет вверх снизу, что мы хотим. В сценарии 2 домен растет вниз сверху, что мы не хотим.
Я ценю, что я мог бы сделать вещи более запутанными для тех из вас, кому удалось схватить вышеуказанно, но это то, что мне дало, чтобы выяснить. Если вы понимаете интуитивно, не беспокойтесь о приведенном выше. Если вы все еще не понимаете, вы к концу этого поста.
Шкала X
Шкала X немного легче выяснить. Нам нужна шкала X, чтобы расти слева направо, имею в виду ширину нашей области диаграммы, а также поля слева и справа.
Домен в этой шкале – это немного более запутанно, хотя, потому что мы больше не имеем дело с цифрами. Вместо этого мы имеем дело с письмами наших категорий.
Чтобы выяснить, как построить этот масштаб, мы сначала должны понимать что-то, называемое порядковым масштабам. Самый быстрый способ понять порядковый масштаб – рассмотреть различия между линейными и порядковыми весами.
На изображении выше вы можете увидеть плохой рисунок двух масштабов. Важно отметить, что линейная шкала – это непрерывный масштаб и порядковый масштаб – это дискретный масштаб.
В примере линейной шкалы, если вы должны были предоставить значение 5.5, он будет сопоставлен на полпути от 5 до 6. Однако, если вы должны были предоставить значение буквы где-то между C и D (который Не существует), D3 понятия не имел, как его сопоставить. Что касается D3, нет никакого способа отображать это значение, потому что вы заявили, что все эти значения являются дискретическими. То есть нет соединительных значений между ними.
Теперь давайте построим ось X.
function getKeys(array) {
return array.map(arrObj = {
return arrObj.category;
});
}
const keys = getKeys(data)
const x = d3.scaleOrdinal()
.domain([...keys])
.range([margin.left, width - margin.right])Если вам интересно, о функции там и переменных клавиш, то есть для извлечения всех категорий, представленных в наших данных, и предоставить ему доменную функцию в качестве массива.
Я мог бы так же легко написать .ОМОЧИКА («А», «B», «C», «D», «E»]) Но тогда мне пришлось бы вручную обновить, что каждый раз, когда мои данные изменились.
Диапазон, как я уже упоминал, нужно расти слева направо. Итак, мы оставляем маржу слева, переместите длину ширины и оставляйте маржу справа.
Создание фактических осей
Теперь у нас есть область диаграммы и определенные масштабы, нам нужно сами настроить оси. Вот как мы это делаем.
const xAxis = d3.axisBottom(x)
Здесь мы создаем Функция называется xaxis, который использует D3.axisbottom Функция с нашей шкалой X, предоставленной в качестве параметра.
Чтобы на самом деле отобразить ось X на нашей таблице, нам нужно сделать следующее
chart.append('g')
.attr('transform', `translate(0, ${height})`)
.call(xAxis)Две вещи, чтобы изучить здесь.
Мы добавляем G элемент на наш график. Мы обсудили G элемент в предыдущем разделе. Затем мы применим преобразование к нашему G элемент. Это преобразование – это то, что все время подходит в D3.
У SVG есть то, что называются функциями преобразования. Есть несколько видов функций преобразования, но тот, который мы заботимся здесь, это Перевести Отказ Перевести принимает два параметра х и y координата. Это означает, сколько единиц пикселей для перемещения G элемент либо в направлении x или y.
Вы можете узнать больше о трансформациях здесь Отказ
Два параметра, которые мы предоставляем Перевести Функция 0 и Высота Отказ Помните, что точка происхождения нашего диаграммы SVG находится в верхнем левом углу. Поскольку мы уже знаем, что это горизонтальная ось, которая начинается в точке происхождения, нам нужно переместить его вертикально вниз по Высота количество единиц.
Если вы не предоставили атрибут преобразования, ось X будет расположен в верхней части вашей графика.
Последняя часть метода цепочки является Позвоните Функция, где Xaxis предоставляется в качестве параметра. Это, вероятно, самый запутанный аспект до сих пор из-за плохого выбора терминологии.
Мы сначала рассмотрим только эти две линии.
.append('g')
.attr('transform', `translate(0, ${height})`)Что вам нужно понять, это то, что когда вы делаете что-то вроде chart.append ('g') это добавляет A G Элемент на элемент диаграммы, выбирает G Элемент, а затем возвращает его. Вы можете проверить это, сделав следующие
const test = chart.append('g')
.attr('transform', `translate(0, ${height})`)
.call(xAxis)console.log(test)Когда результат журнала появляется, вы увидите G элемент под Выбор объект. Это на самом деле то, что позволяет нам делать метод цепочки на Добавить метод. Так как он возвращает G Элемент, мы можем трансформироваться как часть того же метода цепи.
Давайте пойдем на последнюю строку сейчас
.call(xAxis)
Вот что документация D3 говорит о вызов
Итак, мы знаем, что мы используем звонок как функцию, и мы должны пройти его функцию в качестве параметра. Мы знаем это, потому что документация говорит, что она вызывает конкретную функцию ровно один раз. Теперь, другая вещь для понимания, так это то, что Xaxis также является функцией. Вы можете проверить это снова, введя xaxis.
Но, если XAXIS также является функцией, то это требует параметра, а также к ней. Прочитайте документацию для Позвоните Опять же, и вы заметите, что он говорит: «Пассы в этом выборе …». Это означает, что функция XAXIS является неявно называется с G Выбор возвращена от звонка chart.append ('g')
Чтобы объяснить, как Позвоните Работы это именно поэтому мне это не нравится. Там слишком много неявно происходит, что просто кажется черной магией.
Если вы все еще запутались о том, как Позвоните Работает, надеюсь, следующий график очищает его для вас.
Создание оси Y теперь, когда мы знаем, как работает Axis X, намного проще. Мы используем одни и те же принципы, но комменные Axisbottom для Axisleft и слегка изменить функцию перевода.
const yAxis = d3.axisLeft(y);
chart
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.bottom})`)
.call(yAxis);Вы заметите, что трансформировать атрибут имеет Перевести Функция, где y Атрибут установлен на margin.bottom Отказ Если вы вернетесь к диапазону, мы установили для шкалы Y, вы заметите, что мы установили его на Высота - Маржинг. Сотоме Отказ
Когда мы называем D3’s Axisbottom Функция, D3 будет разместить это на Высота - Маржинг. Сотоме , но дно графика на самом деле на Высота Итак, мы добавляем margin.bottom компенсировать.
Размещение баров
Это самая визуальная важная часть графика, потому что именно поэтому пользователь на самом деле получает вид данных.
Во-первых, позвольте мне просто показать вам код, который создаст для нас баров, а затем выпустить его.
chart.selectAll('rect')
.data(data)
.join('rect')
.attr('x', d => x(d.category))
.attr('y', d => y(d.value))
.attr('width', x.bandwidth())
.attr('height', height - y(d.value))
.style('fill', 'steelblue')Первые две строки просты. Selectall . работает так же, как Выберите За исключением того, что он возвращает все возможные выборы определенного элемента.
Призыв .дата Позволяет определить данные, которые вы хотите связать с элементами DOM.
Теперь .join Где суть D3 приходит. Это то, что делает D3 невероятно мощным для создания визуализации с.
Если вы хотите прочитать, что Mike Bonostock (создатель D3) должен сказать на данных, вы можете найти, что здесь Отказ
Далее следует моя попытка объяснять, что .join Функция делает в контексте гистограммы.
Итак, если вы вернетесь назад и посмотрите на данные, которые мы определили ранее в этом посте, вы заметите, что это массив. Причина в том, что это структура данных D3 ожидает.
.join Функция затем принимает каждый элемент массива и Создает соответствующий элемент DOM с этой точкой данных. Отказ
Примечание: .join Функция, используемая ранее, будьте отдельные функции, называемые .enter и .append Отказ Однако этот синтаксис многоочистил. Здесь Это проблема GitHub, где впервые предложил Майк Босток.
Примечание: в графике выше, он должен читать .join (‘rect’) не .join (‘Bar’)
Графика выше иллюстрирует то, что происходит, когда вы выполняете данные соединения. Если вы возьмете массив из 5 элементов и выполните .join ('rect') На нем какое D3 будет делать, это создать элемент RECT SVG для каждого из этих элементов.
Другое, что D3 будет делать, это связать каждую точку данных из вашего массива до соответствующей rect элемент.
const data = [1, 2, 3, 4, 5]
const selection = d3.selectAll('rect')
.data(data)
.join('rect)
selection.each(function(d, i) {
console.log(d)
})
//1, 2, 3, 4, 5Приведенный выше фрагмент кода показывает, как выполнить ведение журнала каждой отдельной точки данных, чтобы удовлетворить свое собственное любопытство.
Вы могли, конечно, заменить rect выше с любым другим элементом SVG, и у вас будет тот же результат.
Отлично, теперь мы знаем, как создавать наши бары, но нам все еще нужно выяснить, как разместить их. Прежде чем продолжить, я рекомендую читать Эта статья MDN о рейз.
Одна вещь, которая сорвала меня много о работе с D3, первоначально пыталась выяснить, как работает система координат SVG.
Если вы хотите более глубокое понимание того, как работают системы координат SVG, проверьте это статья
Графика выше показывает, как различные измерения будут влиять на размещение прямого в координатном пространстве SVG.
Regle SVG-элемент имеет четыре основных атрибута, которые нас будут касаться: X, Y, шириной и высоты.
Вы можете увидеть, как каждый из них относится к координатному пространству SVG на изображении.
Давайте переведем выше в код.
chart
.selectAll("rect")
.data(data)
.join("rect")
.attr("x", d => return x(d.category))
.attr("y", d => return y(d.value))
.attr("width", x.bandwidth())
.attr("height", d => height - y(d.value))
.style("fill", "steelblue");Давайте пройдемся через биты кода после .join вызов.
Когда мы устанавливаем х и y Атрибуты, мы звоним в соответствующие масштабы, которые мы определили ранее. Помните, когда мы определили масштабы, мы сказали, что каждый из них будет функциями, которые могут быть вызваны со значением, чтобы сопоставить его из домена в диапазон. Это именно то, что мы здесь делаем.
Теперь, чтобы понять атрибут Width, нам сначала нужно вернуться к ordinalscale Мы определили. D3 имеет функцию, связанную с каждой шкалой, называемой пропускная способность функция. Это возвращает ширину каждой определенной полосы. D3 Внутренне делает это, разделив диапазон одинаково среди каждого элемента домена.
Итак, мы предоставили массив из 5 символов как домен оси X, и мы устанавливаем диапазон для [Margin.left, ширина - Margin.right] , где ширина и Margin = {Слева: 60, справа: 60}
Итак, у нас есть
(800 - 60 - 60) / 5 = 136 All units are in pixels.
Теперь атрибут высоты – это еще одна вещь, которая давно подтачила меня, потому что я не мог выяснить, почему мы делаем Высота - Y (D.Value) представлять высоту прямого. Конечно, это должно было только что было y (d.value) ?
Это снова отвечает, вспоминая, что координата SVG имеет свою точку происхождения в верхнем левом углу, а ось Y + VE идет вниз.
В графике выше я представил свое понимание того, как рассчитана высота бара. Опять же, если расчет для высоты бара входит для вас интуитивно понятный смысл, не стесняйтесь пропустить это.
Главное заметить в визуальном порядке, состоит в том, что существует разница между осями системы координат SVG и осями нашей диаграммы. Ось Y для системы координат SVG положительна вниз, но ось Y для нашей таблицы положительна вверх.
Это причина, по которой я нарисовал два отдельных набора осей как для х, так и Y. Технически, два оси Y должны быть наложены поверх друг друга, но это затруднит визуально визуально увидеть его. Но вы можете предположить, что они накрыты друг на друга.
Когда мы называем функцию y Scale с y (d.value) мы получаем ценность, что рассчитывает Ось + ve y-ось системы координат SVG, начиная с вершины. Высота показана на стороне, которая является всей длиной оси Y, а затем, что остается Высота - Y (D.Value) , что высота, которую мы присваиваем на бар.
Добавление названий и ярлыков
Теперь мы добираемся до легкого бита. Это просто просто из-за всего, что мы накрыли до сих пор!
Похоже на то, как мы добавили реагирует До нашей SVG до сих пор мы также можем добавить текст как элемент SVG, как ниже:
chart.append('text')
.attr('x', width / 2)
.attr('y', margin.top)
.style('font-size', 32px)
.style('text-anchor', 'middle')
.text('Distribution Among Categories')Элемент SVG Text также имеет х и y атрибут, который работает очень аналогично, как х и y атрибуты rect работай.
Вы можете установить разные атрибуты стиля в текстовый элемент, и вы устанавливаете сам текст, используя .text атрибут.
Теперь давайте разместим этикетку оси Y
chart
.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("y", margin.left / 4)
.text("Values")Хорошо, это немного запутано, поэтому давайте пройдемся через это.
Во-первых, мы применяем трансформировать к элементу и установить это значение для Повернуть (-90) Отказ Что это делает, вращается Сама система координат SVG на 90 градусов.
Примечание. Все, что следует, является моей попыткой обратного инженера, как работает функция вращения. Если я окажу неправ, пожалуйста, извините.
Графика выше показывает, что происходит с системой координат при применении Повернуть (-90) Отказ Теперь вы, вероятно, еще более запутались, потому что отрицательное вращение обычно означает вращение по часовой стрелке. Тем не менее, похоже, я повернул против часовой стрелки здесь.
Ну, помните, что типичная система координат имеет ось Y, указывающую положительно вверх. У нас есть это указывает положительно вниз. Поэтому наши вращения обращаются.
Теперь наша новая ось X оси в противоположном направлении старой оси Y и нашей новой оси Y очков в направлении старой оси X.
Теперь в контексте этой новой информации, глядя на значения х и y атрибуты имеют больше смысла. Поскольку наши новые X очки напротив направления старого Y, мы устанавливаем отрицательное значение для х атрибут.
Вывод
Хорошо, это был довольно пост. Я не предвидел, что это становится вполне настолько массивным, но мы много охватываем много подробно. Я надеюсь, что вам понравилось пройти через этот пост и больше всего на свете, я надеюсь, что у вас будет лучшее понять, как работает D3. Это действительно замечательная библиотека, которая предоставляет вам набор очень мощных инструментов.
Я создал Код SanBox здесь С рабочей версией кода из этого поста. Не стесняйтесь вилить это и играть с ним!
Если вы хотите следовать за мной, вы можете сделать это на Github или Twitter Отказ Если у вас есть какие-либо вопросы, пожалуйста, не стесняйтесь спрашивать.