- PVSM.RU - https://www.pvsm.ru -

TreeMap — это визуализация, где площадь прямоугольника = вес, а цвет = метрика. Отлично подходит для market heatmap (карта рынка), портфелей, иерархий ресурсов и любых “взвешенных деревьев”.
Мне TreeMap понадобился в Angular-проекте под “тепловые карты” и разные иерархические отчёты. Казалось бы — задача стандартная, значит решение должно быть “в один npm install”. Но реальность оказалась неожиданной: готовых TreeMap-решений именно для Angular практически нет.
В итоге я сделал свой standalone компонент и оформил его в npm-пакет: stockchart-treemap.
Пакет бесплатный.
Это не реклама — мне никто не заплатил ни копейки.
Я делал компонент в первую очередь для своих задач, а потом упаковал так, чтобы можно было переиспользовать и не зависеть от внешних UI-комбайнов.
Вот тут самое забавное: обычно в статьях пишут “не подошло потому что тяжелое/дорогое/не то API”. У меня было проще — мне просто не удалось найти нормальный готовый TreeMap под Angular.
Что я видел в процессе поисков:
Почти всё — под чистый JS, React или Vue
TreeMap как визуализация часто встречается в “JS-мире”: примеры на D3-подобных штуках, готовые виджеты, демки. Но это не Angular-компонент, а код, который сам рисует DOM/SVG/Canvas.
В Angular это превращается в вечную обвязку:
ElementRef, ngAfterViewInit, ручной lifecycle,
чистка при destroy,
ручные обновления при изменении данных,
танцы с change detection,
и “почему оно перерисовалось два раза/не там/не тогда”.
То есть формально “решение есть”, но по ощущениям это уже не “встроил компонент”, а “интегрировал чужой движок”.
То, что называется “Angular TreeMap”, часто:
устаревшее (старые версии Angular, нет standalone),
или это тонкая обёртка над JS-ядром без нормального Angular UX (шаблоны, события, типизация, обновления данных — всё “на честном слове”).
Есть варианты в составе больших UI-комбайнов
Иногда это нормально, но лично мне не хотелось тащить “полплатформы” ради одного TreeMap. Особенно когда в проекте уже есть своя дизайн-система и свои компоненты.
Итог: формально решений много, но “поставил и получил нативный Angular-компонент с понятной интеграцией” — мне не попалось. Поэтому я решил сделать компонент так, как мне самому было бы удобно его использовать.
stockchart-treemap — standalone TreeMap компонент для Angular:
без внешних runtime-зависимостей (никаких Kendo UI и т.п.),
с цветовой шкалой по значениям (min/center/max),
с кастомными шаблонами плиток и заголовков,
с несколькими режимами источника данных,
с событиями клика/ховера и “путём” до узла.
StackBlitz: https://stackblitz.com/edit/stackblitz-starters-drgync8z?file=README.md [1]
npm install stockchart-treemap
Peer deps: @angular/core и @angular/common 20.x.
Пример “из коробки”: просто массив + настройки полей.
import { Component } from '@angular/core';
import { TreeMapComponent, TreeMapOptions } from 'stockchart-treemap';
@Component({
standalone: true,
selector: 'app-treemap-demo',
imports: [TreeMapComponent],
template: `
<stockchart-treemap
[data]="data"
[options]="options">
</stockchart-treemap>
`
})
export class TreemapDemoComponent {
data = [
{ name: 'Tech', value: 35, change: 4.2, items: [
{ name: 'A', value: 20, change: 5.1 },
{ name: 'B', value: 15, change: 2.0 }
]},
{ name: 'Energy', value: 25, change: -3.4 },
{ name: 'Health', value: 18, change: 1.6 }
];
options: Partial<TreeMapOptions> = {
textField: 'name',
valueField: 'value',
childrenField: 'items',
// цвет — по "change"
colorValueField: 'change',
colorScale: { min: '#E53935', center: '#F5F5F5', max: '#2E7D32' },
// оставляем пустым, чтобы полагаться на colorScale
colors: []
};
}
Визуализация — это всегда “последний метр” после данных. И данные могут приходить как угодно, поэтому я сделал три режима.
Самый простой: [data]="data".
Если данные живые (RxJS-пайплайн, сокеты, периодические обновления на вашей стороне) — можно отдать data$, компонент подпишется и будет перерисовываться при новых значениях.
Иногда удобнее дать компоненту “как загрузить данные”, а он сам будет обновляться раз в N миллисекунд:
loader = () => this.http.get<Item[]>('/api/treemap');
refreshMs = 5000; // обновление каждые 5 секунд
Плюс есть refreshNow() — полезно для “обновить по кнопке” или после смены фильтров.
Мне нужны были оба сценария:
Когда цвет — это категория (например, разные отделы/типы). Можно задать:
colors: ['#...', '#...', ...]
либо пары [min,max], чтобы генерировать градиенты по уровням.
Классика для “карты рынка”: у элемента есть метрика (например, change), и цвет вычисляется по шкале:
min — красный,
center — нейтральный,
max — зелёный.
Работает так:
задаём colorValueField,
задаём colorScale,
и получаем прогнозируемый градиент.
Если у данных вообще нет цветового поля — оттенки для вложенных уровней считаются автоматически, чтобы дерево было читабельным.
Я сделал два варианта разметки:
squarified (по умолчанию) — стремится к “квадратности” плиток, обычно выглядит лучше.
horizontal / vertical — slice-and-dice, нарезка полосами с чередованием ориентации по уровням.
Это покрывает большую часть практических кейсов без превращения компонента в “комбайн настроек”.
TreeMap без нормальных шаблонов быстро превращается в “красивую мозаику”, из которой нельзя извлечь смысл.
Можно задать отдельно:
titleTemplate — заголовок контейнеров,
tileTemplate — содержимое плитки (обычно лист).
Пример:
<stockchart-treemap
[data]="data"
[options]="options"
[tileTemplate]="tile"
[titleTemplate]="title"
(tileClick)="onTile($event)">
</stockchart-treemap>
<ng-template #title let-item let-node="node">
<div class="title">
{{ item.name }} · {{ node.value | number:'1.0-0' }}
</div>
</ng-template>
<ng-template #tile let-item let-isLeaf="isLeaf">
<div class="tile">
<strong>{{ item.name }}</strong>
<span *ngIf="isLeaf">{{ item.change }}%</span>
</div>
</ng-template>
В интерактивных сценариях важно не только “что кликнули”, но и “где это в дереве”.
Поэтому в событиях есть:
dataItem — исходный объект,
path — цепочка элементов от корня до выбранной ноды.
import { TreeMapEvent } from 'stockchart-treemap';
onTile(ev: TreeMapEvent<MyNode>) {
console.log(ev.dataItem);
console.log(ev.path); // полезно для breadcrumbs / drilldown
}
То, что реально используется чаще всего:
textField / valueField / childrenField / colorField
colorValueField — поле для расчёта colorScale
colors — палитра (если пусто — можно полагаться на colorScale или fallback)
titleSize, showTopLevelTitles
deriveParentValueFromChildren — если у контейнера нет value, можно вывести из детей
roundDecimals — по��огает стабилизировать координаты
minTileSize — минимальный размер плитки для рекурсии
Я сознательно старался избежать “магии”. Внутри всё раскладывается по шагам:
Нормализация данных (любой источник → массив нод).
Подсчёт значений (в т.ч. вычисление контейнеров из детей — опционально).
Layout (squarified / slice-and-dice) рекурсивно разбивает прямоугольник.
Цвет (палитра или интерполяция по шкале).
Рендер + шаблоны + события.
На практике это даёт главное: предсказуемость и возможность легко встроить компонент в проект, не подстраиваясь под чужие решения.
Market heatmap: сектор → индустрия → компания (площадь = вес, цвет = % change).
Портфель: активы → позиции (площадь = доля, цвет = PnL).
Ресурсы: департаменты → проекты → сервисы (площадь = расходы/нагрузка, цвет = отклонение).
Любые метрики в дереве: storage usage, распределение задач, coverage, KPI и т.д.
Если попытаться отображать десятки тысяч нод одновременно — DOM начнёт страдать (как и у большинства UI-визуализаций). Этот компонент рассчитан на нормальные продуктовые сценарии, а не на экстремальные бенчмарки.
Если когда-нибудь понадобится “очень много элементов” — это уже территория Canvas/WebGL/виртуализации.
stockchart-treemap появился из простой причины: TreeMap для Angular почему-то оказался редкостью, а интегрировать JS-виджеты “как есть” в Angular — это отдельный проект по обвязке.
Я сделал standalone компонент, который ставится из npm, нормально живёт в Angular, поддерживает разные источники данных, цветовые шкалы и кастомные шаблоны.
Демо: https://stackblitz.com/edit/stackblitz-starters-drgync8z?file=README.md
[2] Пакет: npm install stockchart-treemap
Автор: youscriptor
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/treemap/441341
Ссылки в тексте:
[1] https://stackblitz.com/edit/stackblitz-starters-drgync8z?file=README.md: https://stackblitz.com/edit/stackblitz-starters-drgync8z?file=README.md
[2] https://stackblitz.com/edit/stackblitz-starters-drgync8z?file=README.md
: https://stackblitz.com/edit/stackblitz-starters-drgync8z?file=README.md%EF%BF%BC
[3] Источник: https://habr.com/ru/articles/983802/?utm_source=habrahabr&utm_medium=rss&utm_campaign=983802
Нажмите здесь для печати.