Статьи

Введение в визуализацию данных с помощью Vue и D3.js

Веб-приложения обычно управляются данными, и часто возникает необходимость визуализации этих данных. Вот где появляются диаграммы и графики. Они упрощают передачу информации, а также демонстрируют корреляции или статистические взаимосвязи. Информация, представленная в форме диаграммы или графика, также легче понять не носителю языка.

В этом уроке мы узнаем, как визуализировать данные в проекте Vue. Для этого мы будем использовать популярную библиотеку D3.js, которая сочетает в себе мощные компоненты визуализации и управляемый данными подход к манипулированию DOM.

Давайте начнем.

Примечание: код этого руководства можно найти на GitHub .

Хотите узнать Vue.js с нуля? Эта статья является выдержкой из нашей Премиум библиотеки. Получите полную коллекцию книг Vue, охватывающих основы, проекты, советы и инструменты и многое другое с SitePoint Premium. Присоединяйтесь сейчас всего за $ 9 / месяц .

Что такое D3?

Как вы можете прочитать на домашней странице проекта , D3.js — это библиотека JavaScript для управления документами на основе данных. D3 поможет вам оживить данные с помощью HTML, SVG и CSS. Акцент на веб-стандартах дает вам все возможности современных браузеров, не привязывая себя к проприетарной среде.

В то время как большинство людей будут ссылаться на D3.js как на библиотеку визуализации данных, это не так . D3 — это скорее фреймворк, состоящий из разных частей — таких как части jQuery (которые помогают нам выбирать и манипулировать элементами DOM), части Lodash, части анимации, части анализа данных и части визуализации данных.

В этом уроке мы будем работать с аспектом визуализации D3. Реальное преимущество D3 при визуализации данных:

  • наличие функций для оформления данных с помощью инструкций по рисованию
  • создание новых доступных для рисования данных из исходных данных
  • генерация путей SVG
  • создание элементов визуализации данных (например, оси) в DOM из ваших данных и методов

Что мы будем строить

Мы хотим создать приложение, которое позволит пользователям искать репозиторий в GitHub, а затем получить визуальное представление о проблемах, открытых на прошлой неделе, которые все еще остаются открытыми. Конечный результат будет выглядеть так:

Финальная Диаграмма

Предпосылки

В этом руководстве предполагается, что у вас есть практические знания по Vue . Предыдущее знание D3.js не требуется, но если вы хотите быстро освоиться, вы можете прочитать наш учебник по D3 в качестве примера .

Вам также необходимо установить Node в вашей системе. Вы можете сделать это, загрузив двоичные файлы для вашей системы с официального сайта или используя менеджер версий .

Наконец, мы будем использовать следующие пакеты для сборки нашего приложения:

  • Vue CLI — для поддержки проекта
  • D3.js — для визуализации наших данных
  • Lodash — предоставляет несколько полезных методов
  • Moment JS — для форматирования даты и времени
  • axios — HTTP-клиент, который помогает нам делать запросы к внешнему API

Новый проект Vue

Я предпочитаю создавать новые проекты Vue, используя Vue CLI. (Если вы не знакомы с Vue CLI, наше руководство для начинающих в этой серии Vue дает полное введение.) Vue CLI предоставляет удобную структуру папок для размещения различных разделов кода, таких как стили, компоненты и так далее.

Убедитесь, что CLI установлен на вашем компьютере:

npm install -g @vue/cli 

Затем создайте новый проект с помощью следующей команды:

 vue create issues-visualization 

Примечание: при создании нового проекта с использованием Vue CLI вам будет предложено выбрать пресет. Для этого конкретного проекта мы просто будем придерживаться значения по умолчанию (Babel + ESLint).

Как только наш новый проект Vue создан, мы перейдем в папку проекта и добавим различные узлы, которые нам понадобятся:

 npm install lodash d3 axios moment 

Несмотря на то, что это простое приложение, которое не имеет много работающих частей, мы все равно будем использовать компонентный подход вместо того, чтобы App.vue весь код из файла App.vue . У нас будет два компонента, компонент App компонент Chart который мы еще не создали.

Компонент App будет обрабатывать выборку данных из GitHub, а затем передавать эти данные в компонент Chart качестве реквизита. Фактическое рисование диаграммы будет происходить внутри компонента Chart . Такое структурирование имеет то преимущество, что, если вы хотите использовать для извлечения данных другую библиотеку, кроме axios, вам будет проще ее поменять. Кроме того, если вы хотите поменять D3 на другую библиотеку диаграмм, это тоже будет проще.

Построение интерфейса поиска

Мы начнем с создания интерфейса поиска, который позволяет пользователям вводить имя репо, которое они хотят видеть визуализированным.

В src/App.vue избавьтесь от всего, что находится внутри <template> и замените содержимое следующим:

 <template> <div id="app"> <form action="#" @submit.prevent="getIssues"> <div class="form-group"> <input type="text" placeholder="owner/repo Name" v-model="repository" class="col-md-2 col-md-offset-5" > </div> </form> </div> </template> 

Здесь у нас есть форма, которая после getIssues предотвращает действие getIssues браузера по умолчанию, а затем вызывает метод getIssues который нам еще предстоит определить. Мы также используем директиву v-model чтобы связать входные данные из формы со свойством repository внутри модели данных нашего экземпляра Vue. Давайте объявим этот repository свойств как пустую строку. Мы также добавим свойство startDate , которое позже будем использовать в качестве первой даты в нашем временном диапазоне:

 import moment from "moment"; import axios from "axios"; export default { name: "app", data() { return { issues: [], repository: "", startDate: null }; }, methods: { getIssues() { // code goes in here } } }; 

Теперь getIssues к созданию метода getIssues :

 getIssues() { this.startDate = moment() .subtract(6, "days") .format("YYYY-MM-DD"); axios .get( `https://api.github.com/search/issues?q=repo:${this.repository}+is:issue+is:open+created:>=${this.startDate}`, { params: { per_page: 100 } } ) .then(response => { const payload = this.getDateRange(); response.data.items.forEach(item => { const key = moment(item.created_at).format("MMM Do YY"); const obj = payload.filter(o => o.day === key)[0]; obj.issues += 1; }); this.issues = payload; console.log(this.issues); }); } 

В приведенном выше блоке кода мы начинаем с установки startDate данных startDate шесть дней назад и форматирования его для использования с API GitHub.

Затем мы используем axios, чтобы сделать запрос API к GitHub, чтобы получить все проблемы для определенного репозитория, которые были открыты на прошлой неделе и которые все еще открыты. Вы можете обратиться к API поиска GitHub, если вам нужно больше примеров того, как найти параметры строки запроса.

При выполнении HTTP-запроса мы устанавливаем количество результатов на 100 на страницу (максимально возможное). Вряд ли есть какие-либо репозитории с более чем 100 новыми выпусками в неделю, так что это должно подойти для наших целей. По умолчанию значение per_page равно 30.

Если запрос завершается успешно, мы используем специальный метод getDateRange для инициализации переменной payload которую мы сможем передать компоненту Chart . Эта полезная нагрузка представляет собой массив объектов, которые будут нравиться так:

 [ {day: "Dec 7th 18", issues: 0}, {day: "Dec 8th 18", issues: 0}, {day: "Dec 9th 18", issues: 0}, {day: "Dec 10th 18", issues: 0}, {day: "Dec 11th 18", issues: 0}, {day: "Dec 12th 18", issues: 0}, {day: "Dec 13th 18", issues: 0} ] 

После этого мы перебираем ответ API. Данные, которые нас интересуют, находятся в ключе items в свойстве data объекта response . Из этого мы берем ключ created_at (который является временной меткой) и форматируем его как свойство day в наших объектах выше. Затем мы ищем соответствующую дату в массиве payload и увеличиваем количество проблем на эту дату на единицу.

Наконец, мы присваиваем массив payload нашему свойству данных issues и регистрируем ответ.

Далее давайте добавим в метод getDateRange :

 methods: { getDateRange() { const startDate = moment().subtract(6, 'days'); const endDate = moment(); const dates = []; while (startDate.isSameOrBefore(endDate)) { dates.push({ day: startDate.format('MMM Do YY'), issues: 0 }); startDate.add(1, 'days'); } return dates; }, getIssues() { ... } } 

Прежде чем мы перейдем к элементу визуализации, давайте также запишем все ошибки, с которыми мы могли бы столкнуться при выполнении нашего запроса к консоли (для целей отладки):

 axios .get( ...) .then(response => { ... }) .catch(error => { console.error(error); }); 

Мы добавим UX для информирования пользователя на случай, если что-то пойдет не так позже.

Пока у нас есть поле ввода, которое позволяет пользователю вводить название организации / хранилища, по которому он хочет искать проблемы. После отправки формы все проблемы, открытые за последнюю неделю, регистрируются на консоли.

Ниже приведен пример того, что было зарегистрировано на консоли для facebook/react :

Консольный вывод

Если вы запустите сервер Vue dev с помощью npm run serve и введете несколько других репозиториев, вы должны увидеть нечто похожее. Если вы застряли для вдохновения, проверьте страницу GentHub’s Trending .

Далее идет забавный момент — визуализация этих данных.

Рисование гистограммы с помощью D3

Ранее мы упоминали, что весь рисунок будет обрабатываться внутри компонента Chart . Давайте создадим компонент:

 touch src/components/Chart.vue 

D3 работает с элементами SVG, и чтобы рисовать что-либо с помощью D3, нам нужно иметь элемент SVG на странице. В нашем недавно созданном компоненте ( src/components/Chart.vue ) давайте создадим тег SVG:

 <template> <div> <svg></svg> </div> </template> 

Для этого конкретного урока мы представим наши данные с помощью гистограммы. Я выбрал столбчатую диаграмму, потому что она представляет визуальный элемент с низкой сложностью, в то время как она обучает основному применению самого D3.js. Гистограмма также является хорошим введением в наиболее важные концепции D3, но при этом весело проводит время!

Прежде чем продолжить, давайте обновим наш компонент App добавив в форму вновь созданный компонент Chart :

 <template> <div id="app"> <form action="#" @submit.prevent="getIssues"> ... </form> <chart :issues="issues"></chart> </div> </template> 

Давайте также зарегистрируем его как компонент:

 import Chart from './components/Chart.vue'; export default { name: "app", components: { Chart }, ... } 

Обратите внимание на то, как мы передаем значение свойства data Chart компоненту Chart в качестве реквизита :

 <chart :issues="issues"></chart> 

Давайте теперь обновим наш компонент Chart чтобы использовать эти данные:

 <script> import * as d3 from "d3"; import _ from "lodash"; export default { props: ["issues"], data() { return { chart: null }; }, watch: { issues(val) { if (this.chart != null) this.chart.remove(); this.renderChart(val); } }, methods: { renderChart(issues_val) { // Chart will be drawn here } } }; </script> 

В приведенном выше блоке кода мы импортируем D3 и Lodash. Затем мы создаем экземпляр свойства данных chart как null . Мы присвоим значение этому, когда начнем рисовать позже.

Поскольку мы хотим рисовать диаграмму каждый раз, когда изменяется значение проблем, мы создали средство наблюдения за issues . Каждый раз, когда это значение изменяется, мы уничтожаем старый график и затем рисуем новый.

Рисование будет происходить внутри метода renderChart . Давайте начнем конкретизировать это:

 renderChart(issues_val) { const margin = 60; const svg_width = 1000; const svg_height = 600; const chart_width = 1000 - 2 * margin; const chart_height = 600 - 2 * margin; const svg = d3 .select("svg") .attr("width", svg_width) .attr("height", svg_height); } 

Здесь мы устанавливаем высоту и ширину только что созданного элемента SVG. Атрибут margin — это то, что мы будем использовать, чтобы придать нашему графику некоторые отступы.

D3 поставляется с возможностями выбора и управления DOM. В этом уроке вы увидите множество d3.select и d3.selectAll . Разница в том, что select вернет первый соответствующий элемент, а selectAll вернет все соответствующие элементы.

Оси

Для гистограмм данные могут быть представлены в вертикальном или горизонтальном формате. D3 поставляется с осевыми методами , которые позволяют нам определять наши оси так, как мы хотим:

  • axisLeft
  • axisTop
  • axisBottom
  • axisRight

Сегодня мы будем создавать вертикальную гистограмму. Для вертикальных гистограмм нам понадобятся только методы axisLeft и axisBottom :

 renderChart(issues_val) { ... this.chart = svg .append("g") .attr("transform", `translate(${margin}, ${margin})`); const yScale = d3 .scaleLinear() .range([chart_height, 0]) .domain([0, _.maxBy(issues_val, "issues").issues]); this.chart .append("g") .call(d3.axisLeft(yScale).ticks(_.maxBy(issues_val, "issues").issues)); const xScale = d3 .scaleBand() .range([0, chart_width]) .domain(issues_val.map(s => s.day)) .padding(0.2); this.chart .append("g") .attr("transform", `translate(0, ${chart_height})`) .call(d3.axisBottom(xScale)); } 

Приведенный выше блок кода рисует оси на элементе SVG. Давайте пройдемся по шагам:

 this.chart = svg.append('g') .attr('transform', `translate(${margin}, ${margin})`); 

Сначала мы указываем, где мы хотим, чтобы наша диаграмма начиналась внутри элемента SVG. При работе с D3 для любого элемента, который мы хотим добавить в SVG, мы обычно вызываем метод append , а затем определяем атрибуты для этого нового элемента.

Чтобы добавить атрибуты к элементу, мы обычно вызываем метод attr , который принимает два параметра. Первый параметр — это атрибут, который мы хотим применить к выбранному элементу DOM, а второй параметр — это требуемое значение или функция обратного вызова, которая возвращает желаемое значение. Здесь мы перемещаем начало графика в позицию 60, 60 SVG:

 const yScale = d3.scaleLinear() .range([chart_height, 0]) .domain([0, _.maxBy(issues_val, 'issues').issues]); this.chart.append('g') .call(d3.axisLeft(yScale) .ticks(_.maxBy(issues_val, 'issues').issues)); 

Этот кодовый блок рисует ось Y, используя шкалы D3, чтобы получить шкалу Y. Шкалы — это функции, которые преобразуют наши данные, увеличивая или уменьшая их значение для лучшей визуализации.

Функция range определяет длину, которая должна быть разделена между пределами входных данных. Возможно, вы заметили, что я использовал высоту в качестве первого параметра, а не ноль при вызове диапазона. Это потому, что система координат SVG начинается с верхнего левого угла. Вы поймете это лучше, когда мы начнем рисовать высоту стержня.

Шкала SVG

С другой стороны, функция domain обозначает минимальное и максимальное значения входных данных. Для этого конкретного набора данных мы хотим начать с нуля до самого высокого значения в нашем наборе данных. Думайте о домене как о входе, а о диапазоне — как о выходе.

После определения y-шкалы мы используем эту шкалу для рисования оси, вызывая метод axisLeft , который принимает y-scale в качестве аргумента.

Фрагмент ниже рисует ось X:

 const xScale = d3.scaleBand() .range([0, chart_width]) .domain(issues_val.map((s) => s.day)) .padding(0.2) this.chart.append('g') .attr('transform', `translate(0, ${chart_height})`) .call(d3.axisBottom(xScale)); 

Для xScale мы используем функцию scaleBand , которая помогает разбить диапазон на полосы и вычислить координаты и ширину полос с дополнительным заполнением. Предполагая, что 3 — это максимальное количество вопросов, поднятых за все даты, выходные данные диаграммы должны выглядеть следующим образом:

Топоры

Если вы протестируете это в своем браузере с помощью кода, который мы уже рассмотрели, вы должны увидеть нечто подобное.

Теперь хватит с осями. Давайте плевать некоторые бары!

Рисование Баров

Для нашей гистограммы ширина полосы будет фиксированной, а высота полосы будет варьироваться в зависимости от размера набора данных:

 renderChart(issues_val) { ... const barGroups = this.chart .selectAll("rect") .data(issues_val) .enter(); barGroups .append("rect") .attr("class", "bar") .attr("x", g => xScale(g.day)) .attr("y", g => yScale(g.issues)) .attr("height", g => chart_height - yScale(g.issues)) .attr("width", xScale.bandwidth()); } 

Давайте рассмотрим, как мы добавили бары. Сначала мы создали элемент barGroups :

 const barGroups = this.chart .selectAll('rect') .data(issues_val) .enter() 

Вызов метода selectAll на нашем графике возвращает пустой выбор / массив, поскольку у нас пока нет прямоугольников на нашем графике. Затем мы связываем метод data , передавая набор данных, который мы хотим визуализировать. Это переводит данные в состояние ожидания для дальнейшей обработки.

Следующим шагом является создание цепочки метода enter . Метод enter просматривает как набор данных, который мы передали в data() и выбор, который мы получаем после вызова selectAll() , а затем пытается найти «совпадения» — больше похоже на отображение между нашими образцами данных и элементами, уже присутствующими в DOM. В данном конкретном случае совпадений не найдено.

Примечание: эта статья является отличным руководством для понимания последовательности select , enter и append при работе с D3 для управления DOM.

Поскольку selectAll('rect') вернул пустой массив, метод enter вернет новый выбор, представляющий элементы в нашем наборе данных.

Обратите внимание, что после создания цепочки enter() каждый элемент в возвращаемом массиве обрабатывается индивидуально. Это означает, что любой метод, связанный с barGroups будет определять поведение отдельных элементов.

 barGroups .append('rect') .attr('class', 'bar') .attr('x', (g) => xScale(g.day)) .attr('y', (g) => yScale(g.issues)) .attr('height', (g) => chart_height - yScale(g.issues)) .attr('width', xScale.bandwidth()); 

Блок кода выше создает прямоугольник для каждого элемента нашего набора данных. Мы даем каждому из этих прямоугольников класс bar .

Чтобы установить координаты x и y для прямоугольников, мы используем функции масштабирования, которые мы определили ранее. Пока что эти прямоугольники сидят друг на друге, и нам нужно дать нашим прямоугольникам некоторую высоту и ширину.

Ширина наших баров будет определяться функцией scaleBand . Привязка функции bandwidth к xScale возвращает рассчитанную ширину полосы из диапазона и отступов, предоставленных для x-scale.

Чтобы установить высоту стержня, мы вычитаем вычисленную y-координату стержня из высоты SVG, чтобы получить правильное представление значения в виде столбца. Помните, что при работе с SVG координаты x и y всегда рассчитываются, начиная с верхнего левого угла.

Добавление ярлыков

Пока у нас есть гистограмма. Но эта диаграмма не очень полезна, так как она не говорит пользователю, что представляет каждая ось. Чтобы придать нашей диаграмме больше контекста, нам нужно добавить метки для осей, а также заголовок диаграммы.

Чтобы добавить метки, мы добавляем текстовые элементы в наш SVG:

 svg .append('text') .attr('class', 'label') .attr('x', -(chart_height / 2) - margin) .attr('y', margin / 2.4) .attr('transform', 'rotate(-90)') .attr('text-anchor', 'middle') .text('Issues opened') svg .append('text') .attr('class', 'label') .attr('x', chart_width / 2 + margin) .attr('y', chart_height + margin * 1.7) .attr('text-anchor', 'middle') .text('Days') svg .append('text') .attr('class', 'title') .attr('x', chart_width / 2 + margin) .attr('y', 40) .attr('text-anchor', 'middle') .text('Issues in the past 1 week') 

Элементы текста могут располагаться с координатами x и y, а выравнивание текста выполняется с помощью атрибута text-anchor . Чтобы добавить сам текст, мы вызываем метод text для элемента text.

Мы можем проверить все, обслуживая наше приложение, а затем искать репо. Поиск любого популярного репо — например, facebook/react :

Реагировать на проблемы

Вернувшись в наш компонент App , теперь мы можем избавиться от оператора console внутри метода getIssues :

 console.log(this.issues) 

Несмотря на то, что наша диаграмма отлично справляется с визуализацией наших данных, многое еще предстоит сделать с точки зрения взаимодействия с пользователем. В следующем разделе мы рассмотрим, как добавить переходы к диаграммам D3.

Добавление переходов

Для этого конкретного графика мы хотим сделать так, чтобы при наведении курсора на элемент бара его оттенок менялся, и количество проблем, представленных столбцом, отображалось в верхней части столбца.

Чтобы это произошло, мы должны выполнить некоторую обработку событий для mouseEnter и mouseLeave для barGroups .

Отредактируйте barGroups кода barGroups над тремя блоками svg :

 barGroups ... .attr("width", xScale.bandwidth()) .on("mouseenter", function(actual, i) { d3.select(this) .transition() .duration(300) .attr("opacity", 0.6) .attr("x", a => xScale(a.day) - 5) .attr("width", xScale.bandwidth() + 10); barGroups .append("text") .attr("class", "value") .attr("x", a => xScale(a.day) + xScale.bandwidth() / 2) .attr("y", a => yScale(a.issues) - 20) .attr("text-anchor", "middle") .text((a, idx) => { return idx !== i ? "" : `${a.issues} issues`; }); }); 

Мы вызываем метод перехода для добавления анимации к элементу при работе с D3.

Каждый раз, когда курсор наводится на полосу, мы уменьшаем непрозрачность полосы и увеличиваем ее ширину на 10 10px . Мы также добавляем текст в верхней части панели с указанием количества проблем, представленных в панели. Этот переход занимает длительность 300 миллисекунд.

Так как мы не хотим оставлять эту панель в новом состоянии, когда мышь уходит, давайте определим событие mouseleave , которое удаляет элементы выбора, которые мы применили в событии mouseenter :

 barGroups ... .attr("width", xScale.bandwidth()) .on("mouseenter", function(actual, i) { ... }) .on("mouseleave", function() { d3.selectAll(".issues").attr("opacity", 1); d3.select(this) .transition() .duration(300) .attr("opacity", 1) .attr("x", a => xScale(a.day)) .attr("width", xScale.bandwidth()); svg.selectAll(".value").remove(); }); . barGroups ... .attr("width", xScale.bandwidth()) .on("mouseenter", function(actual, i) { ... }) .on("mouseleave", function() { d3.selectAll(".issues").attr("opacity", 1); d3.select(this) .transition() .duration(300) .attr("opacity", 1) .attr("x", a => xScale(a.day)) .attr("width", xScale.bandwidth()); svg.selectAll(".value").remove(); }); 

То, что мы делаем выше, это установка непрозрачности полосы на исходное значение, а также удаление текста с панели.

 svg.selectAll('.value').remove(); 

Приведенный выше код удаляет любой текст в DOM, который имеет класс value .

Вот окончательный результат:

Переходы

Если вы попробуете что-то в своем браузере сейчас, вы должны увидеть что-то подобное выше.

Некоторые финальные штрихи UI / UX

Делая запрос в GitHub, мы ожидаем некоторое время загрузки, прежде чем получить ответ от GitHub. В целях UX мы будем информировать пользователя о том, что мы по-прежнему извлекаем результаты из GitHub, мигая на странице с предупреждением о загрузке. В компоненте App добавьте этот фрагмент в раздел HTML своего кода — прямо над компонентом Chart :

 <div class="alert alert-info" v-show="loading">Loading...</div> <chart :issues="issues"></chart> 

Для создания прототипов мы будем использовать Bootstrap. У Vue есть официальный пакет Bootstrap, который мы собираемся установить:

 npm install bootstrap-vue 

Как только пакет будет установлен, нам нужно будет импортировать CSS-классы Bootstrap в наш файл main.js чтобы отобразилось предупреждение:

 [...] import "bootstrap/dist/css/bootstrap.css"; import "bootstrap-vue/dist/bootstrap-vue.css"; 

Мы почти закончили, но нам нужно добавить атрибут loading в нашу модель данных — значение которого будет определять, будет ли отображаться предупреждение о загрузке:

 data() { return { loading: false, ... }; }, 

Каждый раз, когда мы создаем новый запрос, мы устанавливаем значение loading в true , поэтому появляется предупреждение, и затем мы наконец устанавливаем это значение в false для успешных запросов или в случае ошибки приложения:

 getIssues() { this.loading = true; ... axios.get( ... ) .then( ... ) .catch( ... ) .finally(() => (this.loading = false)); } 

Обработка ошибок

В настоящее время мы просто регистрируем любые ошибки, с которыми мы можем столкнуться, на консоли — то, о чем пользователь не знает. Чтобы пользователь знал, что если что-то пойдет не так при отправке запроса, мы высветим предупреждение об ошибке на странице. Добавьте этот фрагмент ниже только что созданного оповещения о загрузке:

 <div class="alert alert-danger" v-show="errored">An error occured</div> <chart :issues="issues"></chart> 

Нам также необходимо обновить нашу модель данных с errored атрибута errored , который мы инициализируем false значением:

 data() { return { loading: false, errored: false, ... }; }, 

Мы установим значение с errored в true каждый раз, когда сталкиваемся с ошибкой. Это должно происходить внутри блока catch:

 getIssues() { this.loading = true; this.errored = false; ... axios.get( ... ) .then( ... ) .catch(error => { console.error(error); this.errored = true; }) .finally( ... ); } 

Некоторые базовые стили

Читатели с орлиными глазами заметят, что на снимке экрана в верхней части этого руководства цвет полосок был синим. Давайте добавим это как последний штрих к нашему компоненту App :

 <style> .bar { fill: #319bbe; } </style> 

Вывод

Поздравляю с этим. Несмотря на то, что у нас есть рабочая гистограмма, мы можем сделать еще больше, чтобы сделать ее более привлекательной. Мы могли бы, например, добавить линии сетки или альтернативно визуализировать эти данные, используя линейный график.

Мы нарисовали простую гистограмму, но D3.js обладает потрясающими возможностями с точки зрения того, что вы можете сделать. Любая визуализация данных, которую вы видели где-либо, включая презентации PowerPoint, вероятно, может быть обработана D3. Эта статья — хорошее чтение для некоторых вещей, которые D3 способен сделать. Вы также можете проверить D3 на GitHub, чтобы увидеть другие варианты использования .