За пределами сетки к тепловой карте

За пределами сетки к тепловой карте

1. Фон и проблема

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

Сначала я думал только о простом общем понятии «тепловая карта», поэтому решил, что наиболее эффективным выбором будет использование ApexCharts, который уже был включен в проект.

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

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

Тепловые карты обычно можно разделить на три основных типа.

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

Этот метод имеет четкую категориальную структуру по осям X и Y и отображает значение конкретной координаты с помощью цвета.

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

Второй - это временный календарный тепловой график, который отображает данные на основе временного потока.

График вкладов GitHub является типичным примером, где поток на основе дат становится ключевой моделью данных. Здесь центральным понятием является не просто отображение координат, а концепция «времени».

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

Это способ визуального представления того, насколько сильно данные сосредоточены в определенных координатах, и он обычно используется в сервисах GIS.

Проблема заключалась в том, что ApexCharts был типичным движком грид-хитмапа.

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

На тот момент объектом реализации являлась тепловая карта календаря, данные для которой должны были быть распределены по неделям в течение 365 дней.

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

В конечном итоге потребовалась отдельная предварительная обработка для отображения годовых данных в координатной системе размером 7 строк и 52 столбца.

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

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

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

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

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

2. Структурные ограничения, возникшие в процессе реализации временного календарного тепловой карты

У нас появилась возможность снова реализовать одинаковую временную календарную тепловую карту, используя MUI Charts, когда мы продолжили стандартизацию внутреннего UI-гидов.

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

Я полагаю, что с точки зрения интеграции с существующей системой дизайна это будет более выгодно, чем ApexCharts.

Однако в процессе реальной реализации вновь возникли структурные проблемы, аналогичные предыдущим.

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

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

То есть, чтобы отображать информацию, такую как "суббота, 15 марта 2025 года" в подсказке, нужно было снова вычислить, какой фактической дате соответствуют текущие координаты. Этот процесс оказался гораздо более сложным, чем ожидалось.

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

В конечном итоге возникла следующая проблема.

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

Во-вторых, была добавлена логика обратного сопоставления данных для простого отображения пользовательского интерфейса.

В-третьих, даже небольшое изменение в дизайне макета повлияло на всю структуру расчета дат.

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

Это было не просто проблема реализации экрана, а вопрос, напрямую связанный с операционной стабильностью.

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

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

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

3. Процесс обзора и выбора специализированной библиотеки для временных рядов

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

Наиболее важными критериями были следующие.

Во-первых, это была проверка, понимает ли библиотека внутреннюю структуру данных даты по умолчанию.

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

В-третьих, это было о том, насколько надёжно поддерживается адаптивная вёрстка.

В-четвёртых, это касалось того, насколько естественно можно интегрировать с общим дизайнерским системой компании.

В результате сравнительного анализа нескольких кандидатов мы пришли к выводу, что react-activity-calendar является наиболее подходящим выбором.

Эта библиотека имела структуру, специально разработанную для тепловых карт календаря временных рядов в стиле вкладов GitHub.

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

Особенно большим преимуществом было то, что если передать только данные о дате, внутренний движок автоматически выполняет вычисления по пакетам и позициям ячеек.

Не нужно было напрямую вычислять, «какая это неделя», «какой это день» или «в каких координатах это должно быть», как в старом подходе.

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

В результате удалось убрать большинство существовавших обходных вычислительных логик.

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

4. Связывание адаптивной обработки и системы дизайна

Еще одной ключевой причиной выбора react-activity-calendar была адаптивная обработка. Обычно для библиотек графиков необходимо напрямую рассчитывать ширину и высоту в зависимости от размера родительского контейнера.

Особенно в UI на основе ячеек часто возникает необходимость отдельно пересчитывать размер ячеек и промежутки в зависимости от изменения размера браузера.

Однако react-activity-calendar автоматически обрабатывал такие вычисления внутренним движком. При изменении размера браузера размер ячеек и промежутки автоматически перераспределялись, сохраняя при этом макет. Благодаря этой структуре не было необходимости реализовывать отдельную логику вычислений на основе ResizeObserver.

В результате код обработки стал значительно проще.

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

Однако этот метод сильно зависит от внутренней структуры классов библиотеки и может легко сломаться при изменении версии.

С другой стороны, react-activity-calendar предлагает декларативную стилистическую структуру на основе объекта темы.

То есть, поскольку цветовые токены можно было напрямую внедрять в виде объектов, мы смогли естественным образом интегрировать их с нашей внутренней общей UI-системой vizend-ui.

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

5. Упорядочение

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

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

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

В конце концов, важно не то, «можно ли осуществить это сейчас», а то, «насколько стабильно это можно поддерживать в рабочей среде».

После перехода на react-activity-calendar удалось избавиться от большинства ненужной предобработки и обратной логики, что значительно улучшило как надежность данных, так и читаемость кода.

Также была обеспечена гораздо более стабильная структура с точки зрения связи адаптивной обработки и системы дизайна.

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

[Справочные материалы]

• Документация по MUI X Heatmap

https://mui.com/x/react-charts/heatmap/

• Демонстрации тепловой карты ApexCharts React

https://apexcharts.com/react-chart-demos/heatmap-charts/

ККамджжинг

Site footer