Визуализация данных - мощное средство визуальной передачи больших объемов информации. Когда дело доходит до наблюдаемости, это также одна из основных функций Datadog (службы мониторинга облачных приложений). Выбор правильной визуализации для ваших данных - важная часть предоставления удобочитаемых представлений о состоянии и производительности ваших систем.
📈 Datadog + визуализация данных
Дизайнерам Datadog требуется нечто большее, чем просто статические диаграммы и красивые визуализации; им нужно идти дальше, углубляясь в детали, исследуя дальше и понимая силы, влияющие на их конструкции. В настоящее время мы используем набор графиков, доступных в Figma, для создания статических визуализаций и передачи их сообщений заинтересованным сторонам. Они хорошо работают в случае разработки пользовательского интерфейса, не зависящего от визуализации.
Но что, если вы хотите разработать новое взаимодействие между графиком и элементом пользовательского интерфейса в вашем дизайне? Вот тогда и пригодится создание прототипа с помощью Framer X.
Framer X - единственный инструмент дизайна, который позволяет создавать компоненты кода, которые можно использовать внутри инструмента дизайна вместе с другими компонентами дизайна и кода.
Вот краткий обзор того, как мы используем Framer X в Datadog для имитации взаимодействия между визуализацией и набором данных:
Следующая часть статьи состоит из двух частей - компонент «Диаграмма» и «Диаграмма + пользовательский интерфейс». Важно понимать, как работает этот компонент, чтобы настроить его под свои нужды и максимально использовать его, поскольку возможности безграничны.
📊 Компонент диаграммы
Почему?
Чтобы разработать правильные шаблоны взаимодействия в Datadog, действительно важно использовать реальные данные или, по крайней мере, их наилучшее моделирование. Как дизайнеры, мы хотим удостовериться, что мы не недооцениваем форму и функцию данных, которые мы хотим представить, сосредотачиваясь исключительно на визуальных эффектах.
Как?
В этом руководстве предполагается, что у вас есть базовая среда разработки с использованием Node, Yarn и Git, а также базовые знания Javascript и React.
Первым делом нужно запустить Framer X.
Чтобы получить диаграммы во Framer X, мы будем использовать библиотеку диаграмм под названием ApexCharts.js, которая имеет интеграцию с React, которая может хорошо работать в данной среде. Давайте установим эту и другие зависимости сначала в папку проекта:
Откройте эту папку в редакторе кода (например, Visual Studio Code) и выполните в терминале следующую команду:
yarn add apexcharts react-apexcharts
Теперь создадим компонент в папке code/
. Назовем его Charting.tsx
и вставим следующий код:
Затем мы настраиваем свойства компонента, а также некоторые значения по умолчанию:
Мы будем использовать крошечную функцию, чтобы сгенерировать фиктивные данные для наших диаграмм:
Большинство свойств, которые мы только что определили, будут переданы как параметры компоненту ApexChart. Но для сохранения состояния нам нужно сделать две реактивные вещи - и для этого мы можем использовать React Hooks:
import { addPropertyControls, Color, ControlType, Frame } from "framer"; import * as React from "react"; import Chart from "react-apexcharts"; enum ChartTypes { Line = "line", Area = "area", Bar = "bar", Heatmap = "heatmap", Radar = "radar" } export interface Props { width?: number; height?: number; points: number; seriesCount: number; chartType: ChartTypes; showToolbar: boolean; showLegend: boolean; showXaxis: boolean; showYaxis: boolean; isStacked: boolean; isHorizontal: boolean; colors: string[]; palette?: string; showStroke: boolean; stroke: number; strokeCurve: "smooth" | "straight" | "stepline"; fillType: "solid" | "gradient" | "pattern"; fillOpacity: number; onDataPointSelection?: () => void; onDataPointMouseEnter?: () => void; onDataPointMouseLeave?: () => void; } export const Charting = function(props) { const { width, height, chartType, isHorizontal, points, seriesCount, fillType, fillOpacity, showStroke, stroke, strokeCurve, palette, showToolbar, showLegend, showXaxis, showYaxis, isStacked, colors } = props; const [series, setSeries] = React.useState(makeSeries(seriesCount, points)); const [mounted, setMounted] = React.useState(false); const options = {}; React.useEffect(() => { setSeries(makeSeries(seriesCount, points)); }, [seriesCount, points]); return ( <> <Frame width={width} height={height} background="none"> <Chart options={options} series={series} type={chartType} width={width} height={height} /> </Frame> </> ); }; function makeSeries(seriesCount: number, points: number) { function genDateValue(n: number) { return Array(n) .fill(1) .map((d, i) => { return { date: new Date(Date.now() - i * 3600000), value: Math.max(250, (Math.random() * 3000) | 0) }; }); } return Array(seriesCount) .fill({ data: [] }) .map(s => { return { data: genDateValue(points).map(v => [v.date, v.value]) }; }); } Charting.defaultProps = { width: 800, height: 600, chartType: ChartTypes.Area, isHorizontal: false, points: 5, seriesCount: 1, fillType: "gradient", fillOpacity: 0.8, stroke: 5, showStroke: true, strokeCurve: "smooth", palette: "palette1", showToolbar: true, showLegend: true, showXaxis: true, showYaxis: false, isStacked: false, colors: [] } as Props;
Давайте передадим в график правильные параметры, чтобы он выглядел лучше:
const options = { colors: colors.length > 0 && palette == "custom" ? colors .map(item => { if (item.startsWith("var(")) return item.match(/rgba?\(.*\d\)/).pop(); return item; }) .map(d => Color.toHexString(Color(d))) : undefined, theme: { palette: palette }, chart: { id: "dogchart", type: chartType, stacked: isStacked, toolbar: { show: showToolbar }, events: { mounted: function(chartContext, config) { setMounted(true); }, dataPointSelection: props.onDataPointSelection, dataPointMouseEnter: props.onDataPointMouseEnter, dataPointMouseLeave: props.onDataPointMouseLeave } }, plotOptions: { bar: { horizontal: isHorizontal } }, fill: { opacity: fillOpacity, type: fillType }, stroke: { show: showStroke, curve: strokeCurve, width: stroke }, xaxis: { type: "datetime", labels: { show: showXaxis } }, yaxis: { show: showYaxis }, dataLabels: { enabled: false }, noData: { text: "No Data" }, legend: { show: showLegend }, tooltip: { fillSeriesColor: true } };
Теперь у нас должна получиться красивая диаграмма:
Сейчас нам нужно исправить две вещи:
- Добавьте миниатюру компонента, которая появляется на боковой панели
- Добавление элементов управления во Framer X, чтобы мы могли настраивать параметры из него
Чтобы добавить миниатюру, нам нужно проверить, был ли компонент смонтирован из состояния, и использовать это для переключения видимости миниатюры.
<> <Frame visible={!mounted} width="100%" height="100%" background="white"> <svg viewBox="0 0 24 16" xmlns="http://www.w3.org/2000/svg"> <text x="0" y="13"> 🐶📊 </text> </svg> </Frame> <Frame width={width} height={height} background="none"> <Chart options={options} series={series} type={chartType} width={width} height={height} /> </Frame> </>
Для добавления элементов управления во Framer X мы будем использовать метод addPropertyControls
из Framer API.
addPropertyControls(Charting, { chartType: { type: ControlType.Enum, options: Object.keys(ChartTypes).map(v => ChartTypes[v]), optionTitles: Object.keys(ChartTypes), title: "Type 📊", defaultValue: ChartTypes.Area }, isHorizontal: { type: ControlType.Boolean, defaultValue: false, hidden(props) { return props.chartType != ChartTypes.Bar; }, title: "Horizontal" }, points: { type: ControlType.Number, min: 0, defaultValue: 5, title: "Points 🔢" }, seriesCount: { type: ControlType.Number, min: 0, defaultValue: 1, title: "Series Count 🔢" }, fillType: { type: ControlType.Enum, options: ["solid", "gradient"], optionTitles: ["Solid", "Gradient"], defaultValue: "solid", title: "Fill type 🌈" }, fillOpacity: { type: ControlType.Number, max: 1, min: 0, step: 0.1, defaultValue: 0.8, title: "↳ Opacity", hidden(props) { return props.fillType != "solid"; } }, showStroke: { type: ControlType.Boolean, defaultValue: true, title: "Stroke 🌀" }, stroke: { type: ControlType.Number, defaultValue: 5, title: "↳ Width", hidden(props) { return props.showStroke == false; } }, strokeCurve: { type: ControlType.Enum, options: ["smooth", "straight", "stepline"], optionTitles: ["Smooth", "Straight", "Stepline"], defaultValue: "smooth", title: "Curve 〰️" }, palette: { type: ControlType.Enum, title: "Palette 🎨", options: [ ...Array(10) .fill("palette") .map((v, i) => `${v}${i + 1}`), "custom" ], optionTitles: [ ...Array(10) .fill("Palette") .map((v, i) => `${v} ${i + 1}`), "Custom" ] }, showXaxis: { type: ControlType.Boolean, defaultValue: true, title: "X-axis 👁" }, showYaxis: { type: ControlType.Boolean, defaultValue: false, title: "Y-axis 👁" }, showLegend: { type: ControlType.Boolean, defaultValue: true, title: "Legend ℹ️" }, showToolbar: { type: ControlType.Boolean, defaultValue: true, title: "Toolbar 🎛" }, isStacked: { type: ControlType.Boolean, defaultValue: false, title: "Stacked 📚" }, colors: { type: ControlType.Array, title: "Colors 🌈", propertyControl: { type: ControlType.Color, defaultValue: Color.toRgbString(Color.random(0.4)) }, hidden(props) { return props.palette != "custom"; } } });
Теперь у нас есть и эскиз, и элементы управления свойствами во Framer X!
Пришло время поиграть с ним или начать проектировать пользовательский интерфейс на его основе.
Если вы хотите начать использовать этот компонент в своем проекте, вы можете получить его в Framer Store:
Вы можете посетить репозиторий кода на GitHub:
📊↔️🕹 Графики + интерфейс
Теперь, когда у нас есть готовый компонент построения диаграмм, давайте применим его в простом интерфейсе и посмотрим, как преодолеть разрыв между дизайном интерфейса и визуализацией данных.
Файл имеет 3 слоя:
- График
- Базовый макет, импортированный из другого инструмента
- Рамка строки при наведении
Чтобы заставить компонент диаграммы взаимодействовать со слоем hover
, нам нужно добавить 2 переопределения; Один считывает выбранную серию с диаграммы, а другой размещает слой hover
в соответствующем месте.
Теперь мы добавляем новую функцию hovered
в файл переопределения, который будет читать выбранную серию и обновлять ее в переменной data
.
import { Override, Data } from "framer"; const TableY = 416; const rowHeight = 32; const data = Data({ hoverPos: TableY, seriesIndex: 0 }); export function hovered(props): Override { return { onDataPointMouseEnter(a, b, c) { console.log("Hoverer Series:", c.seriesIndex); data.seriesIndex = c.seriesIndex + 1; }, onDataPointMouseLeave() { data.seriesIndex = 0; } }; }
Затем мы будем использовать seriesIndex
, чтобы вычислить положение Y слоя hover
и обновить его.
export function calcHoverPos(props): Override { const top = data.hoverPos + data.seriesIndex * (rowHeight + 1); return { top: top, visible: data.seriesIndex > 0 }; }
Убедитесь, что мы применяем функцию calcHoverPos
как переопределение к слою hover
:
Результат
Преодоление разрыва
Визуализация данных - это один из способов, позволяющих аудитории извлекать важный контент из больших объемов информации. При правильном использовании он может эффективно устранить разрыв между техническими результатами и полезными интерпретациями. Короче говоря, это может помочь вам ответить на вопрос: «Почему меня это должно волновать?»
Хотя может потребоваться некоторое время, чтобы узнать, как лучше всего визуализировать ваши данные, это, безусловно, окажется полезным для передачи более эффективного и действенного сообщения. Теперь, когда вы знаете, как работает компонент построения диаграмм, вы можете настроить его, чтобы извлечь из него максимум пользы.
Я надеюсь, что эта статья стала полезным руководством для преодоления разрыва между проектированием пользовательского интерфейса и визуализацией данных. Вы можете обратиться ко мне с вопросами в комментариях или на jgog.in.
Это всего лишь пример проектирования с визуализацией данных в Datadog. Если вы хотите поработать над такими классными вещами? Присоединяйтесь к нам.